diff --git a/core/server/api/db.js b/core/server/api/db.js index 33e36eff17..025b7d05e3 100644 --- a/core/server/api/db.js +++ b/core/server/api/db.js @@ -113,7 +113,7 @@ db = { */ deleteAllContent: function (options) { var tasks, - queryOpts = {columns: 'id'}; + queryOpts = {columns: 'id', context: {internal: true}}; options = options || {}; diff --git a/core/server/middleware/auth.js b/core/server/middleware/auth.js index 412112a17e..17d074cc87 100644 --- a/core/server/middleware/auth.js +++ b/core/server/middleware/auth.js @@ -65,7 +65,7 @@ auth = { delete req.body.client_id; delete req.body.client_secret; - if (!client || client.type !== 'ua') { + if (!client) { errors.logError( i18n.t('errors.middleware.auth.clientAuthenticationFailed'), i18n.t('errors.middleware.auth.clientCredentialsNotValid'), diff --git a/core/server/models/base/index.js b/core/server/models/base/index.js index cd194b113d..76df54fe74 100644 --- a/core/server/models/base/index.js +++ b/core/server/models/base/index.js @@ -233,14 +233,24 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ /** * ### Find All - * Naive find all fetches all the data for a particular model + * Fetches all the data for a particular model * @param {Object} options (optional) * @return {Promise(ghostBookshelf.Collection)} Collection of all Models */ findAll: function findAll(options) { options = this.filterOptions(options, 'findAll'); options.withRelated = _.union(options.withRelated, options.include); - return this.forge().fetchAll(options).then(function then(result) { + + var itemCollection = this.forge(null, {context: options.context}); + + // transforms fictive keywords like 'all' (status:all) into correct allowed values + if (this.processOptions) { + this.processOptions(options); + } + + itemCollection.applyDefaultAndCustomFilters(options); + + return itemCollection.fetchAll(options).then(function then(result) { if (options.include) { _.each(result.models, function each(item) { item.include = options.include; @@ -290,7 +300,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ this.processOptions(options); // Add Filter behaviour - itemCollection.applyFilters(options); + itemCollection.applyDefaultAndCustomFilters(options); // Handle related objects // TODO: this should just be done for all methods @ the API level @@ -306,7 +316,6 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ } else { options.order = self.orderDefaultOptions(); } - return itemCollection.fetchPage(options).then(function formatResponse(response) { var data = {}; diff --git a/core/server/models/plugins/access-rules.js b/core/server/models/plugins/access-rules.js index 3a58f3ab51..c33eae489a 100644 --- a/core/server/models/plugins/access-rules.js +++ b/core/server/models/plugins/access-rules.js @@ -11,6 +11,7 @@ module.exports = function (Bookshelf) { * Cached copy of the context setup for this model instance */ _context: null, + /** * ## Is Public Context? * A helper to determine if this is a public request or not @@ -18,6 +19,10 @@ module.exports = function (Bookshelf) { */ isPublicContext: function isPublicContext() { return !!(this._context && this._context.public); + }, + + isInternalContext: function isInternalContext() { + return !!(this._context && this._context.internal); } }, { diff --git a/core/server/models/plugins/filter.js b/core/server/models/plugins/filter.js index ffb8508fad..d251f38658 100644 --- a/core/server/models/plugins/filter.js +++ b/core/server/models/plugins/filter.js @@ -118,6 +118,7 @@ filter = function filter(Bookshelf) { .query('join', 'users as author', 'author.id', '=', 'posts.author_id'); } }, + /** * ## fetchAndCombineFilters * Helper method, uses the combineFilters util to apply filters to the current model instance @@ -137,6 +138,7 @@ filter = function filter(Bookshelf) { return this; }, + /** * ## Apply Filters * Method which makes the necessary query builder calls (through knex) for the filters set @@ -144,7 +146,7 @@ filter = function filter(Bookshelf) { * @param {Object} options * @returns {Bookshelf.Model} */ - applyFilters: function applyFilters(options) { + applyDefaultAndCustomFilters: function applyDefaultAndCustomFilters(options) { var self = this; // @TODO figure out a better place/way to trigger loading filters diff --git a/core/server/models/post.js b/core/server/models/post.js index fea1f2ade6..61b6299c95 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -394,6 +394,10 @@ Post = ghostBookshelf.Model.extend({ return this.isPublicContext() ? 'status:published' : null; }, defaultFilters: function defaultFilters() { + if (this.isInternalContext()) { + return null; + } + return this.isPublicContext() ? 'page:false' : 'page:false+status:published'; } }, { @@ -458,7 +462,7 @@ Post = ghostBookshelf.Model.extend({ validOptions = { findOne: ['columns', 'importing', 'withRelated', 'require'], findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status', 'staticPages'], - findAll: ['columns'], + findAll: ['columns', 'filter'], add: ['importing'] }; @@ -636,6 +640,7 @@ Post = ghostBookshelf.Model.extend({ if (_.isNumber(postModelOrId) || _.isString(postModelOrId)) { // Grab the original args without the first one origArgs = _.toArray(arguments).slice(1); + // Get the actual post model return this.findOne({id: postModelOrId, status: 'all'}).then(function then(foundPostModel) { // Build up the original args but substitute with actual model diff --git a/core/server/models/user.js b/core/server/models/user.js index e1e6fb20ce..cc5ddc308b 100644 --- a/core/server/models/user.js +++ b/core/server/models/user.js @@ -170,9 +170,15 @@ User = ghostBookshelf.Model.extend({ return role.get('name') === roleName; }); }, + enforcedFilters: function enforcedFilters() { + if (this.isInternalContext()) { + return null; + } + return this.isPublicContext() ? 'status:[' + activeStates.join(',') + ']' : null; }, + defaultFilters: function defaultFilters() { return this.isPublicContext() ? null : 'status:[' + activeStates.join(',') + ']'; } @@ -235,7 +241,8 @@ User = ghostBookshelf.Model.extend({ findOne: ['withRelated', 'status'], setup: ['id'], edit: ['withRelated', 'id'], - findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status'] + findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status'], + findAll: ['filter'] }; if (validOptions[methodName]) { diff --git a/core/test/integration/migration_spec.js b/core/test/integration/migration_spec.js index 08b49347df..af030f539e 100644 --- a/core/test/integration/migration_spec.js +++ b/core/test/integration/migration_spec.js @@ -139,12 +139,13 @@ describe('Database Migration (special functions)', function () { describe('Populate', function () { beforeEach(testUtils.setup()); + it('should populate all fixtures correctly', function (done) { fixtures.populate(loggerStub).then(function () { var props = { posts: Models.Post.findAll({include: ['tags']}), tags: Models.Tag.findAll(), - users: Models.User.findAll({include: ['roles']}), + users: Models.User.findAll({filter: 'status:inactive', context: {internal:true}, include: ['roles']}), clients: Models.Client.findAll(), roles: Models.Role.findAll(), permissions: Models.Permission.findAll({include: ['roles']}) @@ -181,6 +182,7 @@ describe('Database Migration (special functions)', function () { should.exist(result.users); result.users.length.should.eql(1); result.users.at(0).get('name').should.eql('Ghost Owner'); + result.users.at(0).get('status').should.eql('inactive'); result.users.at(0).related('roles').length.should.eql(1); result.users.at(0).related('roles').at(0).get('name').should.eql('Owner'); diff --git a/core/test/integration/model/model_posts_spec.js b/core/test/integration/model/model_posts_spec.js index e5fb06053f..7dec0bdfae 100644 --- a/core/test/integration/model/model_posts_spec.js +++ b/core/test/integration/model/model_posts_spec.js @@ -1324,14 +1324,14 @@ describe('Post Model', function () { // We're going to delete all posts by user 1 var authorData = {id: 1}; - PostModel.findAll().then(function (found) { + PostModel.findAll({context:{internal:true}}).then(function (found) { // There are 50 posts to begin with found.length.should.equal(50); return PostModel.destroyByAuthor(authorData); }).then(function (results) { // User 1 has 13 posts in the database results.length.should.equal(13); - return PostModel.findAll(); + return PostModel.findAll({context:{internal:true}}); }).then(function (found) { // Only 37 should remain found.length.should.equal(37); diff --git a/core/test/unit/models_plugins/filter_spec.js b/core/test/unit/models_plugins/filter_spec.js index b5acb097a5..3af12918a6 100644 --- a/core/test/unit/models_plugins/filter_spec.js +++ b/core/test/unit/models_plugins/filter_spec.js @@ -153,7 +153,7 @@ describe('Filter', function () { }); }); - describe('Apply Filters', function () { + describe('Apply Default and Custom Filters', function () { var fetchSpy, restoreGQL, filterGQL; @@ -174,7 +174,7 @@ describe('Filter', function () { }); it('should call fetchAndCombineFilters if _filters not set', function () { - var result = ghostBookshelf.Model.prototype.applyFilters(); + var result = ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters(); fetchSpy.calledOnce.should.be.true(); should(result._filters).be.null(); @@ -183,7 +183,7 @@ describe('Filter', function () { it('should NOT call fetchAndCombineFilters if _filters IS set', function () { ghostBookshelf.Model.prototype._filters = 'test'; - var result = ghostBookshelf.Model.prototype.applyFilters(); + var result = ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters(); fetchSpy.called.should.be.false(); result._filters.should.eql('test'); @@ -193,7 +193,7 @@ describe('Filter', function () { ghostBookshelf.Model.prototype._filters = {statements: [ {prop: 'title', op: '=', value: 'Hello Word'} ]}; - ghostBookshelf.Model.prototype.applyFilters(); + ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters(); fetchSpy.called.should.be.false(); filterGQL.knexify.called.should.be.true(); @@ -208,7 +208,7 @@ describe('Filter', function () { {prop: 'tags', op: 'IN', value: ['photo', 'video']} ]}; - ghostBookshelf.Model.prototype.applyFilters(); + ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters(); filterGQL.json.printStatements.calledOnce.should.be.true(); filterGQL.json.printStatements.firstCall.args[0].should.eql([ {prop: 'tags', op: 'IN', value: ['photo', 'video']}