Merge branch 'pr/119'

Conflicts:
	core/admin/assets/js/router.js
	core/admin/views/default.hbs
	core/admin/views/settings.hbs
	core/shared/data/fixtures/001.js
This commit is contained in:
Hannah Wolfe 2013-06-09 21:41:07 +01:00
commit b3775feba0
20 changed files with 546 additions and 428 deletions

2
app.js
View File

@ -138,7 +138,7 @@
ghost.app().get('/ghost/editor/:id', auth, admin.editor); ghost.app().get('/ghost/editor/:id', auth, admin.editor);
ghost.app().get('/ghost/editor', auth, admin.editor); ghost.app().get('/ghost/editor', auth, admin.editor);
ghost.app().get('/ghost/content', auth, admin.content); 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', auth, admin.debug.index);
ghost.app().get('/ghost/debug/db/delete/', auth, admin.debug.dbdelete); ghost.app().get('/ghost/debug/db/delete/', auth, admin.debug.dbdelete);
ghost.app().get('/ghost/debug/db/populate/', auth, admin.debug.dbpopulate); ghost.app().get('/ghost/debug/db/populate/', auth, admin.debug.dbpopulate);

View File

@ -21,45 +21,14 @@
router: null router: null
}; };
Ghost.View = Backbone.View.extend({ Ghost.init = function () {
Ghost.router = new Ghost.Router();
// Adds a subview to the current view, which will Backbone.history.start({
// ensure its removal when this view is removed, pushState: true,
// or when view.removeSubviews is called hashChange: false,
addSubview: function (view) { root: '/ghost'
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);
}
});
window.Ghost = Ghost; window.Ghost = Ghost;

View File

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

View File

@ -6,12 +6,41 @@
Ghost.Router = Backbone.Router.extend({ Ghost.Router = Backbone.Router.extend({
routes: { routes: {
'': 'dashboard', '' : 'dashboard',
'content/': 'blog', 'content/' : 'blog',
'editor': 'editor', 'settings/' : 'settings',
'editor/': 'editor', 'settings(/:pane)' : 'settings',
'editor/:id': 'editor' '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 () { dashboard: function () {
var widgets = new Ghost.Collections.Widgets(); var widgets = new Ghost.Collections.Widgets();
@ -418,36 +447,9 @@
} }
}); });
//widgets.fetch().then(function () { //widgets.fetch().then(function () {
Ghost.currentView = new Ghost.Views.Dashboard({ el: '#main', collection: widgets }); 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 });
}
} }
}); });
}()); }());

View File

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

View File

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

View File

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

View File

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

View File

@ -144,8 +144,6 @@
'settings': function (req, res) { 'settings': function (req, res) {
api.settings.browse() api.settings.browse()
.then(function (settings) { .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'),

View File

@ -58,12 +58,17 @@
<!-- // require '/core/admin/assets/js/models/*' --> <!-- // require '/core/admin/assets/js/models/*' -->
<script src="/core/admin/assets/js/models/post.js"></script> <script src="/core/admin/assets/js/models/post.js"></script>
<script src="/core/admin/assets/js/models/widget.js"></script> <script src="/core/admin/assets/js/models/widget.js"></script>
<script src="/core/admin/assets/js/models/settings.js"></script>
<!-- // require '/core/admin/assets/js/views/*' --> <!-- // require '/core/admin/assets/js/views/*' -->
<script src="/core/admin/assets/js/views/base.js"></script>
<script src="/core/admin/assets/js/views/dashboard.js"></script> <script src="/core/admin/assets/js/views/dashboard.js"></script>
<script src="/core/admin/assets/js/views/blog.js"></script> <script src="/core/admin/assets/js/views/blog.js"></script>
<script src="/core/admin/assets/js/views/editor.js"></script> <script src="/core/admin/assets/js/views/editor.js"></script>
<script src="/core/admin/assets/js/views/settings.js"></script>
<script src="/core/admin/assets/js/router.js"></script> <script src="/core/admin/assets/js/router.js"></script>
<script src="/core/admin/assets/js/starter.js"></script>
{{{block "bodyScripts"}}} {{{block "bodyScripts"}}}
<script>
Ghost.init();
</script>
</body> </body>
</html> </html>

View File

@ -1,7 +1,3 @@
{{#contentFor 'bodyScripts'}}
<script src="/core/admin/assets/js/settings.js"></script>
{{/contentFor}}
{{!< default}} {{!< default}}
<div class="wrapper"> <div class="wrapper">
<aside class="settings-sidebar" role="complementary"> <aside class="settings-sidebar" role="complementary">
@ -19,198 +15,198 @@
</ul> </ul>
</nav> </nav>
</aside> </aside>
<section id="general" class="settings-content active"> <div class="settings-container">
<header> <section id="general" class="settings-content active">
<h2 class="title">General</h2> <header>
<section class="page-actions"> <h2 class="title">General</h2>
<button class="button-save">Save</button> <section class="page-actions">
<button class="button-save">Save</button>
</section>
</header>
<section class="content">
{{#with settings}}
<form id="settings-general">
<fieldset>
<label>
<b>Blog Title</b>
<input id="blog-title" type="text" value="{{title}}" />
<p>How your blog name appears on the site</p>
</label>
<label>
<b>Blog Logo</b>
<img src="/core/admin/assets/img/logo.png" alt="logo" height="38" width="381"/>
<p>Display a logo on your site in place of blog title</p>
</label>
<label>
<b>Blog Icon</b>
<img src="/core/admin/assets/img/test-icon.png" alt="logo" height="38" width="38"/>
<p>The icon for your blog, used in your browser tab and elsewhere</p>
</label>
<label>
<b>Email Address</b>
<input id="email-address" type="text" value="{{email}}" />
<p>Address to use for <a href="#">admin notifications</a></p>
<label class="checkbox">
<input type="checkbox" value="1" /> Show my email address on my public profile
</label>
</label>
<label>
<b>URL Structure</b>
<select id="url-structure" name="general[urlstructure]">
<option value="post-name">Simple Post Name</option>
<option value="date-based">Date Based</option>
<option value="number based">Number Based</option>
<option value="custom">Custom...</option>
</select>
</label>
</fieldset>
<hr />
<fieldset>
<label>
<b>Time Zone</b>
<select id="url-structure" name="general[timezone]">
<option value="1">Vienna (UTC+1)</option>
</select>
</label>
</fieldset>
{{/with}}
</form>
</section> </section>
</header>
<section class="content">
{{#with settings}}
<form id="settings-general">
<fieldset>
<label>
<b>Blog Title</b>
<input id="blog-title" type="text" value="{{title}}" />
<p>How your blog name appears on the site</p>
</label>
<label>
<b>Blog Logo</b>
<img src="/core/admin/assets/img/logo.png" alt="logo" height="38" width="381"/>
<p>Display a logo on your site in place of blog title</p>
</label>
<label>
<b>Blog Icon</b>
<img src="/core/admin/assets/img/test-icon.png" alt="logo" height="38" width="38"/>
<p>The icon for your blog, used in your browser tab and elsewhere</p>
</label>
<label>
<b>Email Address</b>
<input id="email-address" type="text" value="john@tryghost.org" />
<p>Address to use for <a href="#">admin notifications</a></p>
<label class="checkbox">
<input type="checkbox" value="1" /> Show my email address on my public profile
</label>
</label>
<label>
<b>URL Structure</b>
<select id="url-structure" name="general[urlstructure]">
<option value="post-name">Simple Post Name</option>
<option value="date-based">Date Based</option>
<option value="number based">Number Based</option>
<option value="custom">Custom...</option>
</select>
</label>
</fieldset>
<hr />
<fieldset>
<label>
<b>Time Zone</b>
<select id="url-structure" name="general[timezone]">
<option value="1">Vienna (UTC+1)</option>
</select>
</label>
</fieldset>
{{/with}}
</form>
</section> </section>
</section> <section id="content" class="settings-content">
<section id="content" class="settings-content"> <header>
<header> <h2 class="title">Content</h2>
<h2 class="title">Content</h2> <section class="page-actions">
<section class="page-actions"> <button class="button-save">Save</button>
<button class="button-save">Save</button> </section>
</header>
<section class="content">
<form id="settings-general">
<fieldset>
<label>
<b>Typography</b>
<select id="url-structure" name="general[urlstructure]">
<option value="post-name">Lato (Light)</option>
</select>
<p>Sexy sans-serif font that will make your toes tickle.</p>
<label class="checkbox">
<input type="checkbox" value="1" /> Load fonts directly from Google
</label>
</label>
<label>
<b>Post Options</b>
<label class="checkbox">
<input type="checkbox" value="1" /> Display Post Meta
<p>Post Author / Date / Views</p>
</label>
<label class="checkbox">
<input type="checkbox" value="1" /> Show Author Box After Post
</label>
<label class="checkbox">
<input type="checkbox" value="1" /> Enable Comments
</label>
</label>
</fieldset>
<hr />
<fieldset>
<label>
<b>SEO Title Pattern</b>
<input id="seo-title" type="text" value="[Post Name] - [Site Title]" />
<p>The pattern used to display your title tags</p>
</label>
<label>
<b>SEO Description Pattern</b>
<input id="seo-description" type="text" value="Auto" />
<p>The pattern used to display your meta descriptions</p>
</label>
<label>
<b>Google+</b>
<label class="checkbox">
<input type="checkbox" value="1" /> Connect to author profile on Google
</label>
</label>
<label>
<b>Home Page Description</b>
<textarea></textarea>
<p>Display a logo on your site in place of blog title</p>
</label>
</fieldset>
</form>
</section> </section>
</header>
<section class="content">
<form id="settings-general">
<fieldset>
<label>
<b>Typography</b>
<select id="url-structure" name="general[urlstructure]">
<option value="post-name">Lato (Light)</option>
</select>
<p>Sexy sans-serif font that will make your toes tickle.</p>
<label class="checkbox">
<input type="checkbox" value="1" /> Load fonts directly from Google
</label>
</label>
<label>
<b>Post Options</b>
<label class="checkbox">
<input type="checkbox" value="1" /> Display Post Meta
<p>Post Author / Date / Views</p>
</label>
<label class="checkbox">
<input type="checkbox" value="1" /> Show Author Box After Post
</label>
<label class="checkbox">
<input type="checkbox" value="1" /> Enable Comments
</label>
</label>
</fieldset>
<hr />
<fieldset>
<label>
<b>SEO Title Pattern</b>
<input id="seo-title" type="text" value="[Post Name] - [Site Title]" />
<p>The pattern used to display your title tags</p>
</label>
<label>
<b>SEO Description Pattern</b>
<input id="seo-description" type="text" value="Auto" />
<p>The pattern used to display your meta descriptions</p>
</label>
<label>
<b>Google+</b>
<label class="checkbox">
<input type="checkbox" value="1" /> Connect to author profile on Google
</label>
</label>
<label>
<b>Home Page Description</b>
<textarea></textarea>
<p>Display a logo on your site in place of blog title</p>
</label>
</fieldset>
</form>
</section> </section>
</section> <section id="users" class="settings-content">
<section id="users" class="settings-content"> <header>
<header> <h2 class="title">Users</h2>
<h2 class="title">Users</h2> <section class="page-actions">
<section class="page-actions"> <button class="button-add">Add User</button>
<button class="button-add">Add User</button> </section>
</header>
<section class="content">
<h6 class="sub">Invited Users</h6>
<ul class="users">
<li class="clearfix">
<div class="user_image"></div>
<div class="user_info">
<span class="name">Some Name</span>
<span class="timestamp">Invitation Sent: 7 hours ago</span>
</div>
</li>
</ul>
<h6 class="sub">Active Users</h6>
<ul class="users">
<li class="clearfix">
<div class="user_image">
<img src="/core/admin/assets/img/user.jpg" alt="user"/>
</div>
<div class="user_info">
<span class="name">Some Name</span>
<span class="timestamp">Last Seen: 7 hours ago</span>
</div>
<label class="admin">Admin</label>
</li>
<li class="clearfix">
<div class="user_image">
<img src="/core/admin/assets/img/user.jpg" alt="user"/>
</div>
<div class="user_info">
<span class="name">Some Name</span>
<span class="timestamp">Last Seen: 2 days ago</span>
</div>
<label class="editor">Editor</label>
</li>
</ul>
</section> </section>
</header>
<section class="content">
<h6 class="sub">Invited Users</h6>
<ul class="users">
<li class="clearfix">
<div class="user_image"></div>
<div class="user_info">
<span class="name">Some Name</span>
<span class="timestamp">Invitation Sent: 7 hours ago</span>
</div>
</li>
</ul>
<h6 class="sub">Active Users</h6>
<ul class="users">
<li class="clearfix">
<div class="user_image">
<img src="/core/admin/assets/img/user.jpg" alt="user"/>
</div>
<div class="user_info">
<span class="name">Some Name</span>
<span class="timestamp">Last Seen: 7 hours ago</span>
</div>
<label class="admin">Admin</label>
</li>
<li class="clearfix">
<div class="user_image">
<img src="/core/admin/assets/img/user.jpg" alt="user"/>
</div>
<div class="user_info">
<span class="name">Some Name</span>
<span class="timestamp">Last Seen: 2 days ago</span>
</div>
<label class="editor">Editor</label>
</li>
</ul>
</section> </section>
</section> <section id="appearance" class="settings-content">
<section id="appearance" class="settings-content"> <header>
<header> <h2 class="title">Appearance</h2>
<h2 class="title">Appearance</h2> </header>
</header> <section class="content">
<section class="content"> <h6 class="sub">Raw json be here</h6>
<h6 class="sub">Raw json be here</h6> <p>Active theme: {{json settings.activeTheme}}</p>
<p>Active theme: {{json settings.activeTheme}}</p> <p>Available themes: {{json availableThemes}}</p>
<p>Available themes: {{json availableThemes}}</p> <p>Available plugins: {{json availablePlugins}}</p>
<p>Available plugins: {{json availablePlugins}}</p> </section>
</section> </section>
</section> </div>
</div> </div>

View File

@ -17,7 +17,9 @@
posts, posts,
users, users,
settings, settings,
requestHandler; requestHandler,
settingsObject,
settingsCollection;
// # Posts // # Posts
posts = { posts = {
@ -59,15 +61,38 @@
}; };
// # Settings // # 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 = { settings = {
browse: function (options) { browse: function (options) {
return dataProvider.Setting.browse(options); return dataProvider.Settings.browse(options).then(settingsObject);
}, },
read: function (options) { 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) { edit: function (settings) {
return dataProvider.Setting.edit(options); settings = settingsCollection(settings);
return dataProvider.Settings.edit(settings).then(settingsObject);
},
add: function (settings) {
settings = settingsCollection(settings);
return dataProvider.Settings.add(settings).then(settingsObject);
} }
}; };

View File

@ -61,6 +61,13 @@ module.exports = {
"updated_by": 1, "updated_by": 1,
"type": "general" "type": "general"
}, },
{
"key": "email",
"value": "john@onolan.org",
"created_by": 1,
"updated_by": 1,
"type": "general"
},
{ {
"key": "activePlugins", "key": "activePlugins",
"value": "", "value": "",

View File

@ -88,7 +88,7 @@
knex.Schema.createTable('settings', function (t) { knex.Schema.createTable('settings', function (t) {
t.increments().primary(); t.increments().primary();
t.string('uuid'); t.string('uuid');
t.string('key'); t.string('key').unique();
t.text('value'); t.text('value');
t.string('type'); t.string('type');
t.date('created_at'); t.date('created_at');

View File

@ -11,7 +11,7 @@
User: require('./user').User, User: require('./user').User,
Role: require('./role').Role, Role: require('./role').Role,
Permission: require('./permission').Permission, Permission: require('./permission').Permission,
Setting: require('./setting').Setting, Settings: require('./settings').Settings,
init: function () { init: function () {
return knex.Schema.hasTable('posts').then(null, function () { return knex.Schema.hasTable('posts').then(null, function () {
// Simple bootstraping of the data model for now. // Simple bootstraping of the data model for now.

View File

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

View File

@ -0,0 +1,42 @@
(function () {
"use strict";
var Settings,
GhostBookshelf = require('./base'),
_ = require('underscore'),
when = require('when');
// Each setting is saved as a separate row in the database,
// but the overlying API treats them as a single key:value mapping
Settings = 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) {
var settings = this;
if (!Array.isArray(_data)) {
_data = [_data];
}
return when.map(_data, function (item) {
// Accept an array of models as input
if (item.toJSON) { item = item.toJSON(); }
return settings.forge({ key: item.key }).fetch().then(function (setting) {
return setting.set('value', item.value).save();
});
});
}
});
module.exports = {
Settings: Settings
};
}());

View File

@ -81,7 +81,7 @@
} }
return when.reject(); return when.reject();
}).otherwise(function() { }).otherwise(function () {
// No permissions loaded, or error loading permissions // No permissions loaded, or error loading permissions
// Still check for permissable without permissions // Still check for permissable without permissions

View File

@ -6,6 +6,6 @@
'role': require('../models/role').Role, 'role': require('../models/role').Role,
'user': require('../models/user').User, 'user': require('../models/user').User,
'permission': require('../models/permission').Permission, 'permission': require('../models/permission').Permission,
'setting': require('../models/setting').Setting 'setting': require('../models/settings').Settings
}; };
}()); }());

View File

@ -8,9 +8,9 @@
helpers = require('./helpers'), helpers = require('./helpers'),
Models = require('../../shared/models'); Models = require('../../shared/models');
describe('Setting Model', function () { describe('Settings Model', function () {
var SettingModel = Models.Setting; var SettingsModel = Models.Settings;
beforeEach(function (done) { beforeEach(function (done) {
helpers.resetData().then(function () { helpers.resetData().then(function () {
@ -19,7 +19,7 @@
}); });
it('can browse', function (done) { it('can browse', function (done) {
SettingModel.browse().then(function (results) { SettingsModel.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -32,7 +32,7 @@
it('can read', function (done) { it('can read', function (done) {
var firstSetting; var firstSetting;
SettingModel.browse().then(function (results) { SettingsModel.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -40,7 +40,7 @@
firstSetting = results.models[0]; firstSetting = results.models[0];
return SettingModel.read(firstSetting.attributes.key); return SettingsModel.read(firstSetting.attributes.key);
}).then(function (found) { }).then(function (found) {
@ -54,22 +54,21 @@
}); });
it('can edit single', function (done) { it('can edit single', function (done) {
var firstPost, var firstSetting;
toEdit = {};
SettingModel.browse().then(function (results) { SettingsModel.browse().then(function (results) {
should.exist(results); should.exist(results);
results.length.should.be.above(0); results.length.should.be.above(0);
firstPost = results.models[0]; firstSetting = results.models[0];
// The edit method has been modified to take an object of // The edit method has been modified to take an object of
// key/value pairs // key/value pairs
toEdit[firstPost.attributes.key] = "new value"; firstSetting.set('value', 'new value');
return SettingModel.edit(toEdit); return SettingsModel.edit(firstSetting);
}).then(function (edited) { }).then(function (edited) {
@ -79,7 +78,7 @@
edited = edited[0]; edited = edited[0];
edited.attributes.key.should.equal(firstPost.attributes.key); edited.attributes.key.should.equal(firstSetting.attributes.key);
edited.attributes.value.should.equal('new value'); edited.attributes.value.should.equal('new value');
done(); done();
@ -88,26 +87,25 @@
}); });
it('can edit multiple', function (done) { it('can edit multiple', function (done) {
var firstPost, var model1,
secondPost, model2,
editedPost, editedModel;
toEdit = {};
SettingModel.browse().then(function (results) { SettingsModel.browse().then(function (results) {
should.exist(results); should.exist(results);
results.length.should.be.above(0); results.length.should.be.above(0);
firstPost = results.models[0]; model1 = results.models[0];
secondPost = results.models[1]; model2 = results.models[1];
// The edit method has been modified to take an object of // The edit method has been modified to take an object of
// key/value pairs // key/value pairs
toEdit[firstPost.attributes.key] = "new value1"; model1.set('value', 'new value1');
toEdit[secondPost.attributes.key] = "new value2"; model2.set('value', 'new value2');
return SettingModel.edit(toEdit); return SettingsModel.edit([model1, model2]);
}).then(function (edited) { }).then(function (edited) {
@ -115,15 +113,15 @@
edited.length.should.equal(2); edited.length.should.equal(2);
editedPost = edited[0]; editedModel = edited[0];
editedPost.attributes.key.should.equal(firstPost.attributes.key); editedModel.attributes.key.should.equal(model1.attributes.key);
editedPost.attributes.value.should.equal('new value1'); editedModel.attributes.value.should.equal('new value1');
editedPost = edited[1]; editedModel = edited[1];
editedPost.attributes.key.should.equal(secondPost.attributes.key); editedModel.attributes.key.should.equal(model2.attributes.key);
editedPost.attributes.value.should.equal('new value2'); editedModel.attributes.value.should.equal('new value2');
done(); done();
@ -136,7 +134,7 @@
value: 'Test Content 1' value: 'Test Content 1'
}; };
SettingModel.add(newSetting).then(function (createdSetting) { SettingsModel.add(newSetting).then(function (createdSetting) {
should.exist(createdSetting); should.exist(createdSetting);
@ -150,7 +148,7 @@
it('can delete', function (done) { it('can delete', function (done) {
var firstSettingId; var firstSettingId;
SettingModel.browse().then(function (results) { SettingsModel.browse().then(function (results) {
should.exist(results); should.exist(results);
@ -158,11 +156,11 @@
firstSettingId = results.models[0].id; firstSettingId = results.models[0].id;
return SettingModel.destroy(firstSettingId); return SettingsModel.destroy(firstSettingId);
}).then(function () { }).then(function () {
return SettingModel.browse(); return SettingsModel.browse();
}).then(function (newResults) { }).then(function (newResults) {