simplifying the model structure, again
This commit is contained in:
parent
f783adbe7f
commit
983c171fb0
@ -76,8 +76,8 @@
|
||||
client: 'sqlite3',
|
||||
connection: {
|
||||
filename: './core/shared/data/tests.db'
|
||||
},
|
||||
debug: true
|
||||
}
|
||||
// debug: true
|
||||
},
|
||||
|
||||
development: {
|
||||
|
@ -8,6 +8,7 @@
|
||||
api = require('../../shared/api'),
|
||||
|
||||
ghost = new Ghost(),
|
||||
dataProvider = ghost.dataProvider,
|
||||
adminNavbar,
|
||||
adminControllers;
|
||||
|
||||
@ -170,7 +171,7 @@
|
||||
});
|
||||
},
|
||||
'dbpopulate': function (req, res) {
|
||||
ghost.dataProvider().populateData(function (error) {
|
||||
dataProvider.populateData(function (error) {
|
||||
if (error) {
|
||||
req.flash('error', error);
|
||||
} else {
|
||||
@ -180,7 +181,7 @@
|
||||
});
|
||||
},
|
||||
'newUser': function (req, res) {
|
||||
ghost.dataProvider().addNewUser(req, function (error) {
|
||||
dataProvider.addNewUser(req, function (error) {
|
||||
if (error) {
|
||||
req.flash('error', error);
|
||||
} else {
|
||||
|
@ -14,10 +14,7 @@
|
||||
_ = require('underscore'),
|
||||
Polyglot = require('node-polyglot'),
|
||||
|
||||
JsonDataProvider = require('./shared/models/dataProvider.json'),
|
||||
jsonDataProvider = new JsonDataProvider(),
|
||||
BookshelfDataProvider = require('./shared/models/dataProvider.bookshelf'),
|
||||
bookshelfDataProvider = new BookshelfDataProvider(),
|
||||
models = require('./shared/models'),
|
||||
ExampleFilter = require('../content/plugins/exampleFilters'),
|
||||
Ghost,
|
||||
instance,
|
||||
@ -49,8 +46,14 @@
|
||||
polyglot;
|
||||
|
||||
if (!instance) {
|
||||
// this.init();
|
||||
instance = this;
|
||||
|
||||
// Holds the filters
|
||||
this.filterCallbacks = [];
|
||||
|
||||
// Holds the filter hooks (that are built in to Ghost Core)
|
||||
this.filters = [];
|
||||
|
||||
plugin = new ExampleFilter(instance).init();
|
||||
|
||||
app = express();
|
||||
@ -64,8 +67,8 @@
|
||||
_.extend(instance, {
|
||||
app: function () { return app; },
|
||||
config: function () { return config; },
|
||||
globals: function () { return instance.globalsData; }, // there's no management here to be sure this has loaded
|
||||
dataProvider: function () { return bookshelfDataProvider; },
|
||||
globals: function () { return instance.globalConfig; }, // there's no management here to be sure this has loaded
|
||||
dataProvider: models,
|
||||
statuses: function () { return statuses; },
|
||||
polyglot: function () { return polyglot; },
|
||||
plugin: function () { return plugin; },
|
||||
@ -84,29 +87,11 @@
|
||||
return instance;
|
||||
};
|
||||
|
||||
Ghost.prototype.init = function() {
|
||||
var initGlobals = jsonDataProvider.save(config.blogData).then(function () {
|
||||
return jsonDataProvider.findAll().then(function (data) {
|
||||
// We must have an instance to be able to call ghost.init(), right?
|
||||
instance.globalsData = data;
|
||||
});
|
||||
});
|
||||
|
||||
return when.all([initGlobals, instance.dataProvider().init()]);
|
||||
Ghost.prototype.init = function () {
|
||||
this.globalConfig = config.blogData;
|
||||
return when.all([instance.dataProvider.init()]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds the filters
|
||||
* @type {Array}
|
||||
*/
|
||||
Ghost.prototype.filterCallbacks = [];
|
||||
|
||||
/**
|
||||
* Holds the filter hooks (that are built in to Ghost Core)
|
||||
* @type {Array}
|
||||
*/
|
||||
Ghost.prototype.filters = [];
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {Function} fn
|
||||
|
@ -13,6 +13,7 @@
|
||||
_ = require('underscore'),
|
||||
|
||||
ghost = new Ghost(),
|
||||
dataProvider = ghost.dataProvider,
|
||||
posts,
|
||||
users,
|
||||
settings,
|
||||
@ -23,50 +24,50 @@
|
||||
// takes filter / pagination parameters
|
||||
// returns a list of posts in a json response
|
||||
browse: function (options) {
|
||||
return ghost.dataProvider().posts.findAll(options);
|
||||
return dataProvider.Post.findAll(options);
|
||||
},
|
||||
// takes an identifier (id or slug?)
|
||||
// returns a single post in a json response
|
||||
read: function (args) {
|
||||
return ghost.dataProvider().posts.findOne(args);
|
||||
return dataProvider.Post.findOne(args);
|
||||
},
|
||||
// takes a json object with all the properties which should be updated
|
||||
// returns the resulting post in a json response
|
||||
edit: function (postData) {
|
||||
return ghost.dataProvider().posts.edit(postData);
|
||||
return dataProvider.Post.edit(postData);
|
||||
},
|
||||
// takes a json object representing a post,
|
||||
// returns the resulting post in a json response
|
||||
add: function (postData) {
|
||||
return ghost.dataProvider().posts.add(postData);
|
||||
return dataProvider.Post.add(postData);
|
||||
},
|
||||
// takes an identifier (id or slug?)
|
||||
// returns a json response with the id of the deleted post
|
||||
destroy: function (args) {
|
||||
return ghost.dataProvider().posts.destroy(args.id);
|
||||
return dataProvider.Post.destroy(args.id);
|
||||
}
|
||||
};
|
||||
|
||||
// # Users
|
||||
users = {
|
||||
add: function (postData) {
|
||||
return ghost.dataProvider().users.add(postData);
|
||||
return dataProvider.User.add(postData);
|
||||
},
|
||||
check: function (postData) {
|
||||
return ghost.dataProvider().users.check(postData);
|
||||
return dataProvider.User.check(postData);
|
||||
}
|
||||
};
|
||||
|
||||
// # Settings
|
||||
settings = {
|
||||
browse: function (options) {
|
||||
return ghost.dataProvider().settings.browse(options);
|
||||
return dataProvider.Setting.browse(options);
|
||||
},
|
||||
read: function (options) {
|
||||
return ghost.dataProvider().settings.read(options.key);
|
||||
return dataProvider.Setting.read(options.key);
|
||||
},
|
||||
edit: function (options) {
|
||||
return ghost.dataProvider().settings.edit(options);
|
||||
return dataProvider.Setting.edit(options);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
var when = require('when'),
|
||||
knex = require('../../models/knex_init'),
|
||||
knex = require('../../models/base').Knex,
|
||||
fixtures = require('../fixtures/001'),
|
||||
up,
|
||||
down;
|
||||
|
96
core/shared/models/base.js
Normal file
96
core/shared/models/base.js
Normal file
@ -0,0 +1,96 @@
|
||||
(function () {
|
||||
|
||||
"use strict";
|
||||
|
||||
var GhostBookshelf,
|
||||
Bookshelf = require('bookshelf'),
|
||||
config = require('../../../config');
|
||||
|
||||
// Initializes Bookshelf as its own instance, so we can modify the Models and not mess up
|
||||
// others' if they're using the library outside of ghost.
|
||||
GhostBookshelf = Bookshelf.Initialize('ghost', config.database[process.env.NODE_ENV || 'development']);
|
||||
|
||||
// The Base Model which other Ghost objects will inherit from,
|
||||
// including some convenience functions as static properties on the model.
|
||||
GhostBookshelf.Model = GhostBookshelf.Model.extend({
|
||||
|
||||
// Base prototype properties will go here
|
||||
|
||||
}, {
|
||||
|
||||
/**
|
||||
* Naive find all
|
||||
* @param options (optional)
|
||||
*/
|
||||
findAll: function (options) {
|
||||
options = options || {};
|
||||
return GhostBookshelf.Collection.forge([], {model: this}).fetch(options);
|
||||
},
|
||||
|
||||
browse: function () {
|
||||
return this.findAll.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Naive find one where args match
|
||||
* @param args
|
||||
* @param options (optional)
|
||||
*/
|
||||
findOne: function (args, options) {
|
||||
options = options || {};
|
||||
return this.forge(args).fetch(options);
|
||||
},
|
||||
|
||||
read: function () {
|
||||
return this.findOne.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Naive edit
|
||||
* @param editedObj
|
||||
* @param options (optional)
|
||||
*/
|
||||
edit: function (editedObj, options) {
|
||||
options = options || {};
|
||||
return this.forge({id: editedObj.id}).fetch(options).then(function (foundObj) {
|
||||
return foundObj.set(editedObj).save();
|
||||
});
|
||||
},
|
||||
|
||||
update: function () {
|
||||
return this.edit.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Naive create
|
||||
* @param editedObj
|
||||
* @param options (optional)
|
||||
*/
|
||||
add: function (newObj, options) {
|
||||
options = options || {};
|
||||
return this.forge(newObj).save(options);
|
||||
},
|
||||
|
||||
create: function () {
|
||||
return this.add.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Naive destroy
|
||||
* @param _identifier
|
||||
* @param options (optional)
|
||||
*/
|
||||
destroy: function (_identifier, options) {
|
||||
options = options || {};
|
||||
return this.forge({id: _identifier}).destroy(options);
|
||||
},
|
||||
|
||||
'delete': function () {
|
||||
return this.destroy.apply(this, arguments);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
module.exports = GhostBookshelf;
|
||||
|
||||
}());
|
@ -1,74 +0,0 @@
|
||||
/*global require, module */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var _ = require('underscore'),
|
||||
BookshelfBase;
|
||||
|
||||
/**
|
||||
* The base class for interacting with bookshelf models/collections.
|
||||
* Provides naive implementations of CRUD/BREAD operations.
|
||||
*/
|
||||
BookshelfBase = function (model, collection) {
|
||||
// Bind the 'this' value for all our functions since they get messed
|
||||
// up by the when.call
|
||||
_.bindAll(this, 'findAll', 'browse', 'findOne', 'read', 'edit', 'add', 'destroy');
|
||||
|
||||
this.model = model;
|
||||
this.collection = collection;
|
||||
};
|
||||
|
||||
/**
|
||||
* Naive find all
|
||||
* @param args (optional)
|
||||
* @param opts (optional)
|
||||
*/
|
||||
BookshelfBase.prototype.findAll = BookshelfBase.prototype.browse = function (opts) {
|
||||
opts = opts || {};
|
||||
return this.collection.forge().fetch(opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Naive find one where args match
|
||||
* @param args
|
||||
* @param opts (optional)
|
||||
*/
|
||||
BookshelfBase.prototype.findOne = BookshelfBase.prototype.read = function (args, opts) {
|
||||
opts = opts || {};
|
||||
return this.model.forge(args).fetch(opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Naive edit
|
||||
* @param editedObj
|
||||
* @param opts (optional)
|
||||
*/
|
||||
BookshelfBase.prototype.edit = BookshelfBase.prototype.update = function (editedObj, opts) {
|
||||
opts = opts || {};
|
||||
return this.model.forge({id: editedObj.id}).fetch(opts).then(function (foundObj) {
|
||||
return foundObj.set(editedObj).save();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Naive add
|
||||
* @param newObj
|
||||
* @param opts (optional)
|
||||
*/
|
||||
BookshelfBase.prototype.add = BookshelfBase.prototype.create = function (newObj, opts) {
|
||||
opts = opts || {};
|
||||
return this.model.forge(newObj).save(opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Naive destroy
|
||||
* @param _identifier
|
||||
* @param opts (optional)
|
||||
*/
|
||||
BookshelfBase.prototype.destroy = BookshelfBase.prototype['delete'] = function (_identifier, opts) {
|
||||
opts = opts || {};
|
||||
return this.model.forge({id: _identifier}).destroy(opts);
|
||||
};
|
||||
|
||||
module.exports = BookshelfBase;
|
||||
}());
|
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Provides access to data via the Bookshelf ORM
|
||||
*/
|
||||
|
||||
/*globals module, require, process */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var _ = require('underscore'),
|
||||
knex = require('./knex_init'),
|
||||
PostsProvider = require('./dataProvider.bookshelf.posts'),
|
||||
UsersProvider = require('./dataProvider.bookshelf.users'),
|
||||
SettingsProvider = require('./dataProvider.bookshelf.settings'),
|
||||
DataProvider,
|
||||
instance,
|
||||
defaultOptions = {
|
||||
autoInit: false
|
||||
};
|
||||
|
||||
DataProvider = function (options) {
|
||||
options = _.defaults(options || {}, defaultOptions);
|
||||
|
||||
if (!instance) {
|
||||
instance = this;
|
||||
|
||||
if (options.autoInit) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
DataProvider.prototype.init = function () {
|
||||
return knex.Schema.hasTable('posts').then(null, function () {
|
||||
// Simple bootstraping of the data model for now.
|
||||
var migration = require('../data/migration/001');
|
||||
|
||||
return migration.down().then(function () {
|
||||
return migration.up();
|
||||
});
|
||||
}).then(function () {
|
||||
console.log('DataProvider ready');
|
||||
});
|
||||
};
|
||||
|
||||
DataProvider.prototype.posts = new PostsProvider();
|
||||
DataProvider.prototype.users = new UsersProvider();
|
||||
DataProvider.prototype.settings = new SettingsProvider();
|
||||
|
||||
module.exports = DataProvider;
|
||||
}());
|
@ -1,91 +0,0 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var _ = require('underscore'),
|
||||
util = require('util'),
|
||||
models = require('./models'),
|
||||
Bookshelf = require('bookshelf'),
|
||||
BaseProvider = require('./dataProvider.bookshelf.base'),
|
||||
PostsProvider;
|
||||
|
||||
/**
|
||||
* The Posts data provider implementation for Bookshelf.
|
||||
*/
|
||||
PostsProvider = function () {
|
||||
BaseProvider.call(this, models.Post, models.Posts);
|
||||
};
|
||||
|
||||
util.inherits(PostsProvider, BaseProvider);
|
||||
|
||||
/**
|
||||
* Find results by page - returns an object containing the
|
||||
* information about the request (page, limit), along with the
|
||||
* info needed for pagination (pages, total).
|
||||
*
|
||||
* {
|
||||
* posts: [
|
||||
* {...}, {...}, {...}
|
||||
* ],
|
||||
* page: __,
|
||||
* limit: __,
|
||||
* pages: __,
|
||||
* total: __
|
||||
* }
|
||||
*
|
||||
* @params opts
|
||||
*/
|
||||
PostsProvider.prototype.findPage = function (opts) {
|
||||
var postCollection;
|
||||
|
||||
// Allow findPage(n)
|
||||
if (!_.isObject(opts)) {
|
||||
opts = {page: opts};
|
||||
}
|
||||
|
||||
opts = _.defaults(opts || {}, {
|
||||
page: 1,
|
||||
limit: 15,
|
||||
where: null
|
||||
});
|
||||
postCollection = this.collection.forge();
|
||||
|
||||
// If there are where conditionals specified, add those
|
||||
// to the query.
|
||||
if (opts.where) {
|
||||
postCollection.query('where', opts.where);
|
||||
}
|
||||
|
||||
// Set the limit & offset for the query, fetching
|
||||
// with the opts (to specify any eager relations, etc.)
|
||||
// Omitting the `page`, `limit`, `where` just to be sure
|
||||
// aren't used for other purposes.
|
||||
return postCollection
|
||||
.query('limit', opts.limit)
|
||||
.query('offset', opts.limit * (opts.page - 1))
|
||||
.fetch(_.omit(opts, 'page', 'limit', 'where'))
|
||||
.then(function (collection) {
|
||||
var qb;
|
||||
|
||||
// After we're done, we need to figure out what
|
||||
// the limits are for the pagination values.
|
||||
qb = Bookshelf.Knex(_.result(collection, 'tableName'));
|
||||
|
||||
if (opts.where) {
|
||||
qb.where(opts.where);
|
||||
}
|
||||
|
||||
return qb.count(_.result(collection, 'idAttribute')).then(function (resp) {
|
||||
var totalPosts = resp[0].aggregate;
|
||||
return {
|
||||
posts: collection.toJSON(),
|
||||
page: opts.page,
|
||||
limit: opts.limit,
|
||||
pages: Math.ceil(totalPosts / opts.limit),
|
||||
total: totalPosts
|
||||
};
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = PostsProvider;
|
||||
}());
|
@ -1,37 +0,0 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var _ = require('underscore'),
|
||||
when = require('when'),
|
||||
util = require('util'),
|
||||
models = require('./models'),
|
||||
BaseProvider = require('./dataProvider.bookshelf.base'),
|
||||
SettingsProvider;
|
||||
|
||||
/**
|
||||
* The Posts data provider implementation for Bookshelf.
|
||||
*/
|
||||
SettingsProvider = function () {
|
||||
BaseProvider.call(this, models.Setting, models.Settings);
|
||||
};
|
||||
|
||||
util.inherits(SettingsProvider, BaseProvider);
|
||||
|
||||
SettingsProvider.prototype.read = function (_key) {
|
||||
// Allow for just passing the key instead of attributes
|
||||
if (_.isString(_key)) {
|
||||
_key = { key: _key };
|
||||
}
|
||||
return BaseProvider.prototype.read.call(this, _key);
|
||||
};
|
||||
|
||||
SettingsProvider.prototype.edit = function (_data) {
|
||||
return when.all(_.map(_data, function (value, key) {
|
||||
return this.model.forge({ key: key }).fetch().then(function (setting) {
|
||||
return setting.set('value', value).save();
|
||||
});
|
||||
}, this));
|
||||
};
|
||||
|
||||
module.exports = SettingsProvider;
|
||||
}());
|
@ -1,70 +0,0 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var util = require('util'),
|
||||
_ = require('underscore'),
|
||||
bcrypt = require('bcrypt-nodejs'),
|
||||
models = require('./models.js'),
|
||||
when = require('when'),
|
||||
nodefn = require('when/node/function'),
|
||||
BaseProvider = require('./dataProvider.bookshelf.base.js'),
|
||||
UsersProvider;
|
||||
|
||||
/**
|
||||
* The Users data provider implementation for Bookshelf.
|
||||
*/
|
||||
UsersProvider = function () {
|
||||
BaseProvider.call(this, models.User, models.Users);
|
||||
};
|
||||
|
||||
util.inherits(UsersProvider, BaseProvider);
|
||||
|
||||
/**
|
||||
* Naive user add
|
||||
* @param _user
|
||||
*
|
||||
* Hashes the password provided before saving to the database.
|
||||
*/
|
||||
UsersProvider.prototype.add = function (_user) {
|
||||
var self = this,
|
||||
// Clone the _user so we don't expose the hashed password unnecessarily
|
||||
userData = _.extend({}, _user);
|
||||
|
||||
|
||||
return self.model.forge({email_address: userData.email_address}).fetch().then(function (user) {
|
||||
if (!!user.attributes.email_address) {
|
||||
when.reject(new Error('A user with that email address already exists.'));
|
||||
}
|
||||
|
||||
return nodefn.call(bcrypt.hash, _user.password, null, null).then(function (hash) {
|
||||
userData.password = hash;
|
||||
return BaseProvider.prototype.add.call(self, userData);
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* User check
|
||||
* @param _userdata
|
||||
*
|
||||
* Finds the user by email, and check's the password
|
||||
*/
|
||||
UsersProvider.prototype.check = function (_userdata) {
|
||||
return this.model.forge({
|
||||
email_address: _userdata.email
|
||||
}).fetch().then(function (user) {
|
||||
if (!!user.attributes.email_address) {
|
||||
return nodefn.call(bcrypt.compare, _userdata.pw, user.get('password')).then(function (matched) {
|
||||
if (!matched) {
|
||||
return when.reject(new Error('Passwords do not match'));
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
return when.reject(new Error('We do not have a record for such user.'));
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = UsersProvider;
|
||||
}());
|
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Dummy dataProvider returns hardcoded JSON data until we finish migrating settings data to a datastore
|
||||
*/
|
||||
|
||||
/*globals module, require */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var _ = require('underscore'),
|
||||
when = require('when'),
|
||||
DataProvider,
|
||||
instance;
|
||||
|
||||
DataProvider = function () {
|
||||
if (!instance) {
|
||||
instance = this;
|
||||
_.extend(instance, {
|
||||
data: [],
|
||||
findAll: function() {
|
||||
return when(instance.data);
|
||||
},
|
||||
save: function (globals) {
|
||||
_.each(globals, function (global, key) {
|
||||
instance.data[key] = global;
|
||||
}, instance);
|
||||
|
||||
return when(globals);
|
||||
}
|
||||
});
|
||||
}
|
||||
return instance;
|
||||
};
|
||||
|
||||
module.exports = DataProvider;
|
||||
}());
|
26
core/shared/models/index.js
Normal file
26
core/shared/models/index.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*global require, module */
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var GhostBookshelf = require('./base'),
|
||||
knex = GhostBookshelf.Knex;
|
||||
|
||||
module.exports = {
|
||||
Post: require('./post').Post,
|
||||
User: require('./user').User,
|
||||
Setting: require('./setting').Setting,
|
||||
init: function () {
|
||||
return knex.Schema.hasTable('posts').then(null, function () {
|
||||
// Simple bootstraping of the data model for now.
|
||||
var migration = require('../data/migration/001');
|
||||
return migration.down().then(function () {
|
||||
return migration.up();
|
||||
});
|
||||
}).then(function () {
|
||||
console.log('models loaded');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}());
|
@ -1,11 +0,0 @@
|
||||
/*global require, module, process */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var knex = require('knex'),
|
||||
config = require('../../../config');
|
||||
|
||||
knex.Initialize(config.database[process.env.NODE_ENV || 'development']);
|
||||
|
||||
module.exports = knex;
|
||||
}());
|
134
core/shared/models/post.js
Normal file
134
core/shared/models/post.js
Normal file
@ -0,0 +1,134 @@
|
||||
(function () {
|
||||
|
||||
"use strict";
|
||||
|
||||
var Post,
|
||||
Posts,
|
||||
_ = require('underscore'),
|
||||
Showdown = require('showdown'),
|
||||
converter = new Showdown.converter(),
|
||||
User = require('./user').User,
|
||||
GhostBookshelf = require('./base');
|
||||
|
||||
Post = GhostBookshelf.Model.extend({
|
||||
|
||||
tableName: 'posts',
|
||||
|
||||
hasTimestamps: true,
|
||||
|
||||
initialize: function () {
|
||||
this.on('creating', this.creating, this);
|
||||
this.on('saving', this.saving, this);
|
||||
},
|
||||
|
||||
saving: function () {
|
||||
if (!this.get('title')) {
|
||||
throw new Error('Post title cannot be blank');
|
||||
}
|
||||
this.set('content_html', converter.makeHtml(this.get('content')));
|
||||
|
||||
// refactoring of ghost required in order to make these details available here
|
||||
// this.set('language', this.get('language') || ghost.config().defaultLang);
|
||||
// this.set('status', this.get('status') || ghost.statuses().draft);
|
||||
},
|
||||
|
||||
creating: function () {
|
||||
if (!this.get('slug')) {
|
||||
this.generateSlug();
|
||||
}
|
||||
},
|
||||
|
||||
generateSlug: function () {
|
||||
return this.set('slug', this.get('title').replace(/\:/g, '').replace(/\s/g, '-').toLowerCase());
|
||||
},
|
||||
|
||||
user: function () {
|
||||
return this.belongsTo(User, 'created_by');
|
||||
}
|
||||
|
||||
}, {
|
||||
|
||||
/**
|
||||
* Find results by page - returns an object containing the
|
||||
* information about the request (page, limit), along with the
|
||||
* info needed for pagination (pages, total).
|
||||
*
|
||||
* {
|
||||
* posts: [
|
||||
* {...}, {...}, {...}
|
||||
* ],
|
||||
* page: __,
|
||||
* limit: __,
|
||||
* pages: __,
|
||||
* total: __
|
||||
* }
|
||||
*
|
||||
* @params opts
|
||||
*/
|
||||
findPage: function (opts) {
|
||||
var postCollection;
|
||||
|
||||
// Allow findPage(n)
|
||||
if (!_.isObject(opts)) {
|
||||
opts = {page: opts};
|
||||
}
|
||||
|
||||
opts = _.defaults(opts || {}, {
|
||||
page: 1,
|
||||
limit: 15,
|
||||
where: null
|
||||
});
|
||||
postCollection = Posts.forge();
|
||||
|
||||
// If there are where conditionals specified, add those
|
||||
// to the query.
|
||||
if (opts.where) {
|
||||
postCollection.query('where', opts.where);
|
||||
}
|
||||
|
||||
// Set the limit & offset for the query, fetching
|
||||
// with the opts (to specify any eager relations, etc.)
|
||||
// Omitting the `page`, `limit`, `where` just to be sure
|
||||
// aren't used for other purposes.
|
||||
return postCollection
|
||||
.query('limit', opts.limit)
|
||||
.query('offset', opts.limit * (opts.page - 1))
|
||||
.fetch(_.omit(opts, 'page', 'limit', 'where'))
|
||||
.then(function (collection) {
|
||||
var qb;
|
||||
|
||||
// After we're done, we need to figure out what
|
||||
// the limits are for the pagination values.
|
||||
qb = GhostBookshelf.Knex(_.result(collection, 'tableName'));
|
||||
|
||||
if (opts.where) {
|
||||
qb.where(opts.where);
|
||||
}
|
||||
|
||||
return qb.count(_.result(collection, 'idAttribute')).then(function (resp) {
|
||||
var totalPosts = resp[0].aggregate;
|
||||
return {
|
||||
posts: collection.toJSON(),
|
||||
page: opts.page,
|
||||
limit: opts.limit,
|
||||
pages: Math.ceil(totalPosts / opts.limit),
|
||||
total: totalPosts
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Posts = GhostBookshelf.Collection.extend({
|
||||
|
||||
model: Post
|
||||
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
Post: Post,
|
||||
Posts: Posts
|
||||
};
|
||||
|
||||
}());
|
45
core/shared/models/setting.js
Normal file
45
core/shared/models/setting.js
Normal file
@ -0,0 +1,45 @@
|
||||
(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
|
||||
};
|
||||
|
||||
}());
|
78
core/shared/models/user.js
Normal file
78
core/shared/models/user.js
Normal file
@ -0,0 +1,78 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var User,
|
||||
Users,
|
||||
_ = require('underscore'),
|
||||
when = require('when'),
|
||||
nodefn = require('when/node/function'),
|
||||
bcrypt = require('bcrypt-nodejs'),
|
||||
Posts = require('./post').Posts,
|
||||
GhostBookshelf = require('./base');
|
||||
|
||||
User = GhostBookshelf.Model.extend({
|
||||
|
||||
tableName: 'users',
|
||||
|
||||
hasTimestamps: true,
|
||||
|
||||
posts: function () {
|
||||
return this.hasMany(Posts, 'created_by');
|
||||
}
|
||||
|
||||
}, {
|
||||
|
||||
/**
|
||||
* Naive user add
|
||||
* @param _user
|
||||
*
|
||||
* Hashes the password provided before saving to the database.
|
||||
*/
|
||||
add: function (_user) {
|
||||
var User = this,
|
||||
// Clone the _user so we don't expose the hashed password unnecessarily
|
||||
userData = _.extend({}, _user);
|
||||
|
||||
return this.forge({email_address: userData.email_address}).fetch().then(function (user) {
|
||||
if (!!user.attributes.email_address) {
|
||||
when.reject(new Error('A user with that email address already exists.'));
|
||||
}
|
||||
|
||||
return nodefn.call(bcrypt.hash, _user.password, null, null).then(function (hash) {
|
||||
userData.password = hash;
|
||||
return GhostBookshelf.Model.add.call(User, userData);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* User check
|
||||
* @param _userdata
|
||||
*
|
||||
* Finds the user by email, and check's the password
|
||||
*/
|
||||
check: function (_userdata) {
|
||||
return this.forge({
|
||||
email_address: _userdata.email
|
||||
}).fetch({require: true}).then(function (user) {
|
||||
return nodefn.call(bcrypt.compare, _userdata.pw, user.get('password')).then(function (matched) {
|
||||
if (!matched) {
|
||||
return when.reject(new Error('Passwords do not match'));
|
||||
}
|
||||
return user;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Users = GhostBookshelf.Collection.extend({
|
||||
model: User
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
User: User,
|
||||
Users: Users
|
||||
};
|
||||
|
||||
}());
|
@ -6,21 +6,20 @@
|
||||
var _ = require("underscore"),
|
||||
should = require('should'),
|
||||
helpers = require('./helpers'),
|
||||
PostProvider = require('../../shared/models/dataProvider.bookshelf.posts');
|
||||
Models = require('../../shared/models');
|
||||
|
||||
describe('Bookshelf PostsProvider', function () {
|
||||
describe('Bookshelf Post Model', function () {
|
||||
|
||||
var posts;
|
||||
var PostModel = Models.Post;
|
||||
|
||||
beforeEach(function (done) {
|
||||
helpers.resetData().then(function () {
|
||||
posts = new PostProvider();
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('can browse', function (done) {
|
||||
posts.browse().then(function (results) {
|
||||
PostModel.browse().then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
results.length.should.equal(2);
|
||||
@ -32,14 +31,14 @@
|
||||
it('can read', function (done) {
|
||||
var firstPost;
|
||||
|
||||
posts.browse().then(function (results) {
|
||||
PostModel.browse().then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
results.length.should.be.above(0);
|
||||
|
||||
firstPost = results.models[0];
|
||||
|
||||
return posts.read({slug: firstPost.attributes.slug});
|
||||
return PostModel.read({slug: firstPost.attributes.slug});
|
||||
}).then(function (found) {
|
||||
should.exist(found);
|
||||
|
||||
@ -52,7 +51,7 @@
|
||||
it('can edit', function (done) {
|
||||
var firstPost;
|
||||
|
||||
posts.browse().then(function (results) {
|
||||
PostModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -60,7 +59,7 @@
|
||||
|
||||
firstPost = results.models[0];
|
||||
|
||||
return posts.edit({id: firstPost.id, title: "new title"});
|
||||
return PostModel.edit({id: firstPost.id, title: "new title"});
|
||||
|
||||
}).then(function (edited) {
|
||||
|
||||
@ -79,7 +78,7 @@
|
||||
content: 'Test Content 1'
|
||||
};
|
||||
|
||||
posts.add(newPost).then(function (createdPost) {
|
||||
PostModel.add(newPost).then(function (createdPost) {
|
||||
should.exist(createdPost);
|
||||
|
||||
createdPost.attributes.title.should.equal(newPost.title, "title is correct");
|
||||
@ -92,7 +91,7 @@
|
||||
|
||||
it('can delete', function (done) {
|
||||
var firstPostId;
|
||||
posts.browse().then(function (results) {
|
||||
PostModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -100,11 +99,11 @@
|
||||
|
||||
firstPostId = results.models[0].id;
|
||||
|
||||
return posts.destroy(firstPostId);
|
||||
return PostModel.destroy(firstPostId);
|
||||
|
||||
}).then(function () {
|
||||
|
||||
return posts.browse();
|
||||
return PostModel.browse();
|
||||
|
||||
}).then(function (newResults) {
|
||||
var ids, hasDeletedId;
|
||||
@ -126,7 +125,7 @@
|
||||
|
||||
helpers.insertMorePosts().then(function () {
|
||||
|
||||
return posts.findPage({page: 2});
|
||||
return PostModel.findPage({page: 2});
|
||||
|
||||
}).then(function (paginationResult) {
|
||||
|
||||
@ -138,7 +137,7 @@
|
||||
|
||||
paginationResult.pages.should.equal(4);
|
||||
|
||||
return posts.findPage({page: 5});
|
||||
return PostModel.findPage({page: 5});
|
||||
|
||||
}).then(function (paginationResult) {
|
||||
|
||||
@ -150,7 +149,7 @@
|
||||
|
||||
paginationResult.pages.should.equal(4);
|
||||
|
||||
return posts.findPage({limit: 30});
|
||||
return PostModel.findPage({limit: 30});
|
||||
|
||||
}).then(function (paginationResult) {
|
||||
|
||||
@ -162,7 +161,7 @@
|
||||
|
||||
paginationResult.pages.should.equal(2);
|
||||
|
||||
return posts.findPage({limit: 10, page: 2, where: {language: 'fr'}});
|
||||
return PostModel.findPage({limit: 10, page: 2, where: {language: 'fr'}});
|
||||
|
||||
}).then(function (paginationResult) {
|
||||
|
||||
|
@ -6,21 +6,20 @@
|
||||
var _ = require("underscore"),
|
||||
should = require('should'),
|
||||
helpers = require('./helpers'),
|
||||
SettingProvider = require('../../shared/models/dataProvider.bookshelf.settings');
|
||||
Models = require('../../shared/models');
|
||||
|
||||
describe('Bookshelf SettingsProvider', function () {
|
||||
describe('Bookshelf Setting Model', function () {
|
||||
|
||||
var settings;
|
||||
var SettingModel = Models.Setting;
|
||||
|
||||
beforeEach(function (done) {
|
||||
helpers.resetData().then(function () {
|
||||
settings = new SettingProvider();
|
||||
done();
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('can browse', function (done) {
|
||||
settings.browse().then(function (results) {
|
||||
SettingModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -33,7 +32,7 @@
|
||||
it('can read', function (done) {
|
||||
var firstSetting;
|
||||
|
||||
settings.browse().then(function (results) {
|
||||
SettingModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -41,7 +40,7 @@
|
||||
|
||||
firstSetting = results.models[0];
|
||||
|
||||
return settings.read(firstSetting.attributes.key);
|
||||
return SettingModel.read(firstSetting.attributes.key);
|
||||
|
||||
}).then(function (found) {
|
||||
|
||||
@ -58,7 +57,7 @@
|
||||
var firstPost,
|
||||
toEdit = {};
|
||||
|
||||
settings.browse().then(function (results) {
|
||||
SettingModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -70,7 +69,7 @@
|
||||
// key/value pairs
|
||||
toEdit[firstPost.attributes.key] = "new value";
|
||||
|
||||
return settings.edit(toEdit);
|
||||
return SettingModel.edit(toEdit);
|
||||
|
||||
}).then(function (edited) {
|
||||
|
||||
@ -94,7 +93,7 @@
|
||||
editedPost,
|
||||
toEdit = {};
|
||||
|
||||
settings.browse().then(function (results) {
|
||||
SettingModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -108,7 +107,7 @@
|
||||
toEdit[firstPost.attributes.key] = "new value1";
|
||||
toEdit[secondPost.attributes.key] = "new value2";
|
||||
|
||||
return settings.edit(toEdit);
|
||||
return SettingModel.edit(toEdit);
|
||||
|
||||
}).then(function (edited) {
|
||||
|
||||
@ -137,7 +136,7 @@
|
||||
value: 'Test Content 1'
|
||||
};
|
||||
|
||||
settings.add(newSetting).then(function (createdSetting) {
|
||||
SettingModel.add(newSetting).then(function (createdSetting) {
|
||||
|
||||
should.exist(createdSetting);
|
||||
|
||||
@ -151,7 +150,7 @@
|
||||
it('can delete', function (done) {
|
||||
var firstSettingId;
|
||||
|
||||
settings.browse().then(function (results) {
|
||||
SettingModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -159,11 +158,11 @@
|
||||
|
||||
firstSettingId = results.models[0].id;
|
||||
|
||||
return settings.destroy(firstSettingId);
|
||||
return SettingModel.destroy(firstSettingId);
|
||||
|
||||
}).then(function () {
|
||||
|
||||
return settings.browse();
|
||||
return SettingModel.browse();
|
||||
|
||||
}).then(function (newResults) {
|
||||
|
||||
|
@ -6,22 +6,21 @@
|
||||
var _ = require('underscore'),
|
||||
should = require('should'),
|
||||
helpers = require('./helpers'),
|
||||
UserProvider = require('../../shared/models/dataProvider.bookshelf.users');
|
||||
Models = require('../../shared/models');
|
||||
|
||||
describe('Bookshelf UsersProvider', function () {
|
||||
describe('Bookshelf User Model', function () {
|
||||
|
||||
var users;
|
||||
var UserModel = Models.User;
|
||||
|
||||
beforeEach(function (done) {
|
||||
helpers.resetData().then(function () {
|
||||
users = new UserProvider();
|
||||
done();
|
||||
});
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('can browse', function (done) {
|
||||
|
||||
users.browse().then(function (results) {
|
||||
UserModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -35,7 +34,7 @@
|
||||
it('can read', function (done) {
|
||||
var firstUser;
|
||||
|
||||
users.browse().then(function (results) {
|
||||
UserModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -43,7 +42,7 @@
|
||||
|
||||
firstUser = results.models[0];
|
||||
|
||||
return users.read({email_address: firstUser.attributes.email_address});
|
||||
return UserModel.read({email_address: firstUser.attributes.email_address});
|
||||
|
||||
}).then(function (found) {
|
||||
|
||||
@ -60,7 +59,7 @@
|
||||
it('can edit', function (done) {
|
||||
var firstUser;
|
||||
|
||||
users.browse().then(function (results) {
|
||||
UserModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -68,7 +67,7 @@
|
||||
|
||||
firstUser = results.models[0];
|
||||
|
||||
return users.edit({id: firstUser.id, url: "some.newurl.com"});
|
||||
return UserModel.edit({id: firstUser.id, url: "some.newurl.com"});
|
||||
|
||||
}).then(function (edited) {
|
||||
|
||||
@ -87,7 +86,7 @@
|
||||
email_address: "test@test1.com"
|
||||
};
|
||||
|
||||
users.add(userData).then(function (createdUser) {
|
||||
UserModel.add(userData).then(function (createdUser) {
|
||||
|
||||
should.exist(createdUser);
|
||||
|
||||
@ -101,7 +100,7 @@
|
||||
it('can delete', function (done) {
|
||||
var firstUserId;
|
||||
|
||||
users.browse().then(function (results) {
|
||||
UserModel.browse().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
@ -109,11 +108,11 @@
|
||||
|
||||
firstUserId = results.models[0].id;
|
||||
|
||||
return users.destroy(firstUserId);
|
||||
return UserModel.destroy(firstUserId);
|
||||
|
||||
}).then(function () {
|
||||
|
||||
return users.browse();
|
||||
return UserModel.browse();
|
||||
|
||||
}).then(function (newResults) {
|
||||
var ids, hasDeletedId;
|
||||
|
@ -1,20 +0,0 @@
|
||||
/*globals describe, beforeEach, it*/
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var should = require('should'),
|
||||
DataProvider = require('../../shared/models/dataProvider.json');
|
||||
|
||||
describe("dataProvider.json", function () {
|
||||
|
||||
it("is a singleton", function () {
|
||||
var provider1 = new DataProvider(),
|
||||
provider2 = new DataProvider();
|
||||
|
||||
should.strictEqual(provider1, provider2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
@ -20,26 +20,28 @@
|
||||
it("uses init() to initialize", function (done) {
|
||||
var ghost = new Ghost(),
|
||||
fakeDataProvider = {
|
||||
init: function() {
|
||||
init: function () {
|
||||
return when.resolve();
|
||||
}
|
||||
},
|
||||
dataProviderInitSpy = sinon.spy(fakeDataProvider, "init");
|
||||
dataProviderInitSpy = sinon.spy(fakeDataProvider, "init"),
|
||||
oldDataProvder = ghost.dataProvider;
|
||||
|
||||
// Stub out the dataProvider
|
||||
sinon.stub(ghost, "dataProvider", function () {
|
||||
return fakeDataProvider;
|
||||
});
|
||||
ghost.dataProvider = fakeDataProvider;
|
||||
|
||||
should.not.exist(ghost.globals());
|
||||
|
||||
ghost.init().then(function () {
|
||||
|
||||
should.exist(ghost.globals());
|
||||
|
||||
dataProviderInitSpy.called.should.equal(true);
|
||||
|
||||
ghost.dataProvider = oldDataProvder;
|
||||
|
||||
done();
|
||||
}, done);
|
||||
}).then(null, done);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Use 'testing' Ghost config; unless we are running on travis (then show queries for debugging)
|
||||
process.env.NODE_ENV = process.env.TRAVIS ? 'travis' : 'testing';
|
||||
|
||||
var knex = require('knex'),
|
||||
var knex = require('../../shared/models/base').Knex,
|
||||
migrations = {
|
||||
one: require("../../shared/data/migration/001")
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user