diff --git a/core/server/api/users.js b/core/server/api/users.js
index f9c408bcc9..e6eba10ec5 100644
--- a/core/server/api/users.js
+++ b/core/server/api/users.js
@@ -255,9 +255,22 @@ users = {
destroy: function destroy(options) {
return canThis(options.context).destroy.user(options.id).then(function () {
return users.read(options).then(function (result) {
- return dataProvider.User.destroy(options).then(function () {
+ return dataProvider.Base.transaction(function (t) {
+ options.transacting = t;
+ dataProvider.Post.destroyByAuthor(options).then(function () {
+ return dataProvider.User.destroy(options);
+ }).then(function () {
+ t.commit();
+ }).catch(function (error) {
+ t.rollback(error);
+ });
+ }).then(function () {
return result;
+ }, function (error) {
+ return when.reject(new errors.InternalServerError(error));
});
+ }, function (error) {
+ return errors.handleAPIError(error);
});
}).catch(function (error) {
return errors.handleAPIError(error);
diff --git a/core/server/models/post.js b/core/server/models/post.js
index 125da72b8e..2b3481c9b2 100644
--- a/core/server/models/post.js
+++ b/core/server/models/post.js
@@ -521,6 +521,31 @@ Post = ghostBookshelf.Model.extend({
});
},
+
+ /**
+ * ### destroyByAuthor
+ * @param {[type]} options has context and id. Context is the user doing the destroy, id is the user to destroy
+ */
+ destroyByAuthor: function (options) {
+ var postCollection = Posts.forge(),
+ authorId = options.id;
+
+ options = this.filterOptions(options, 'destroyByAuthor');
+ if (authorId) {
+ return postCollection.query('where', 'author_id', '=', authorId).fetch(options).then(function (results) {
+ return when.map(results.models, function (post) {
+ return post.related('tags').detach(null, options).then(function () {
+ return post.destroy(options);
+ });
+ });
+ }, function (error) {
+ return when.reject(new errors.InternalServerError(error.message || error));
+ });
+ }
+ return when.reject(new errors.NotFoundError('No user found'));
+ },
+
+
permissible: function (postModelOrId, action, context, loadedPermissions, hasUserPermission, hasAppPermission) {
var self = this,
postModel = postModelOrId,
diff --git a/core/server/models/user.js b/core/server/models/user.js
index cbc327801d..bcbd85142c 100644
--- a/core/server/models/user.js
+++ b/core/server/models/user.js
@@ -745,7 +745,7 @@ User = ghostBookshelf.Model.extend({
contextUser = ctxUser;
return User.findOne({id: object.id});
}).then(function (user) {
-
+
var currentRoles = user.toJSON().roles;
if (!_.contains(currentRoles, adminRole.id)) {
return when.reject(new errors.ValidationError('Only administrators can be assigned the owner role.'));
diff --git a/core/test/integration/model/model_posts_spec.js b/core/test/integration/model/model_posts_spec.js
index 314842755d..d9a7a3a860 100644
--- a/core/test/integration/model/model_posts_spec.js
+++ b/core/test/integration/model/model_posts_spec.js
@@ -12,70 +12,57 @@ var testUtils = require('../../utils'),
describe('Post Model', function () {
// Keep the DB clean
- before(testUtils.teardown);
- afterEach(testUtils.teardown);
- beforeEach(testUtils.setup('owner', 'posts', 'apps'));
- should.exist(PostModel);
+ describe('Single author posts', function () {
- function checkFirstPostData(firstPost) {
- should.not.exist(firstPost.author_id);
- firstPost.author.should.be.an.Object;
- firstPost.fields.should.be.an.Array;
- firstPost.tags.should.be.an.Array;
- firstPost.author.name.should.equal(DataGenerator.Content.users[0].name);
- firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
- firstPost.created_at.should.be.an.instanceof(Date);
- firstPost.created_by.should.be.an.Object;
- firstPost.updated_by.should.be.an.Object;
- firstPost.published_by.should.be.an.Object;
- firstPost.created_by.name.should.equal(DataGenerator.Content.users[0].name);
- firstPost.updated_by.name.should.equal(DataGenerator.Content.users[0].name);
- firstPost.published_by.name.should.equal(DataGenerator.Content.users[0].name);
- firstPost.tags[0].name.should.equal(DataGenerator.Content.tags[0].name);
- }
+ before(testUtils.teardown);
+ afterEach(testUtils.teardown);
+ beforeEach(testUtils.setup('owner', 'posts', 'apps'));
- it('can findAll', function (done) {
- PostModel.findAll().then(function (results) {
- should.exist(results);
- results.length.should.be.above(1);
+ should.exist(PostModel);
- done();
- }).catch(done);
- });
+ function checkFirstPostData(firstPost) {
+ should.not.exist(firstPost.author_id);
+ firstPost.author.should.be.an.Object;
+ firstPost.fields.should.be.an.Array;
+ firstPost.tags.should.be.an.Array;
+ firstPost.author.name.should.equal(DataGenerator.Content.users[0].name);
+ firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
+ firstPost.created_at.should.be.an.instanceof(Date);
+ firstPost.created_by.should.be.an.Object;
+ firstPost.updated_by.should.be.an.Object;
+ firstPost.published_by.should.be.an.Object;
+ firstPost.created_by.name.should.equal(DataGenerator.Content.users[0].name);
+ firstPost.updated_by.name.should.equal(DataGenerator.Content.users[0].name);
+ firstPost.published_by.name.should.equal(DataGenerator.Content.users[0].name);
+ firstPost.tags[0].name.should.equal(DataGenerator.Content.tags[0].name);
+ }
- it('can findAll, returning all related data', function (done) {
- var firstPost;
-
- PostModel.findAll({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
- .then(function (results) {
+ it('can findAll', function (done) {
+ PostModel.findAll().then(function (results) {
should.exist(results);
- results.length.should.be.above(0);
- firstPost = results.models[0].toJSON();
- checkFirstPostData(firstPost);
+ results.length.should.be.above(1);
done();
}).catch(done);
- });
+ });
- it('can findPage (default)', function (done) {
- PostModel.findPage().then(function (results) {
- should.exist(results);
+ it('can findAll, returning all related data', function (done) {
+ var firstPost;
- results.meta.pagination.page.should.equal(1);
- results.meta.pagination.limit.should.equal(15);
- results.meta.pagination.pages.should.equal(1);
- results.posts.length.should.equal(4);
+ PostModel.findAll({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
+ .then(function (results) {
+ should.exist(results);
+ results.length.should.be.above(0);
+ firstPost = results.models[0].toJSON();
+ checkFirstPostData(firstPost);
- done();
- }).catch(done);
- });
+ done();
+ }).catch(done);
+ });
- it('can findPage, returning all related data', function (done) {
- var firstPost;
-
- PostModel.findPage({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
- .then(function (results) {
+ it('can findPage (default)', function (done) {
+ PostModel.findPage().then(function (results) {
should.exist(results);
results.meta.pagination.page.should.equal(1);
@@ -83,397 +70,444 @@ describe('Post Model', function () {
results.meta.pagination.pages.should.equal(1);
results.posts.length.should.equal(4);
+ done();
+ }).catch(done);
+ });
+
+ it('can findPage, returning all related data', function (done) {
+ var firstPost;
+
+ PostModel.findPage({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
+ .then(function (results) {
+ should.exist(results);
+
+ results.meta.pagination.page.should.equal(1);
+ results.meta.pagination.limit.should.equal(15);
+ results.meta.pagination.pages.should.equal(1);
+ results.posts.length.should.equal(4);
+
+ firstPost = results.posts[0];
+
+ checkFirstPostData(firstPost);
+
+ done();
+ }).catch(done);
+ });
+
+
+ it('can findOne', function (done) {
+ var firstPost;
+
+ PostModel.findPage().then(function (results) {
+ should.exist(results);
+ should.exist(results.posts);
+ results.posts.length.should.be.above(0);
firstPost = results.posts[0];
- checkFirstPostData(firstPost);
+ return PostModel.findOne({slug: firstPost.slug});
+ }).then(function (found) {
+ should.exist(found);
+ found.attributes.title.should.equal(firstPost.title);
done();
}).catch(done);
- });
+ });
+ it('can findOne, returning all related data', function (done) {
+ var firstPost;
+ // TODO: should take author :-/
+ PostModel.findOne({}, {include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
+ .then(function (result) {
+ should.exist(result);
+ firstPost = result.toJSON();
- it('can findOne', function (done) {
- var firstPost;
+ checkFirstPostData(firstPost);
- PostModel.findPage().then(function (results) {
- should.exist(results);
- should.exist(results.posts);
- results.posts.length.should.be.above(0);
- firstPost = results.posts[0];
+ done();
+ }).catch(done);
+ });
- return PostModel.findOne({slug: firstPost.slug});
- }).then(function (found) {
- should.exist(found);
- found.attributes.title.should.equal(firstPost.title);
+ it('can edit', function (done) {
+ var firstPost = 1;
- done();
- }).catch(done);
- });
+ PostModel.findOne({id: firstPost}).then(function (results) {
+ var post;
+ should.exist(results);
+ post = results.toJSON();
+ post.id.should.equal(firstPost);
+ post.title.should.not.equal('new title');
- it('can findOne, returning all related data', function (done) {
- var firstPost;
- // TODO: should take author :-/
- PostModel.findOne({}, {include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
- .then(function (result) {
- should.exist(result);
- firstPost = result.toJSON();
-
- checkFirstPostData(firstPost);
+ return PostModel.edit({title: 'new title'}, _.extend(context, {id: firstPost}));
+ }).then(function (edited) {
+ should.exist(edited);
+ edited.attributes.title.should.equal('new title');
done();
}).catch(done);
- });
-
- it('can edit', function (done) {
- var firstPost = 1;
-
- PostModel.findOne({id: firstPost}).then(function (results) {
- var post;
- should.exist(results);
- post = results.toJSON();
- post.id.should.equal(firstPost);
- post.title.should.not.equal('new title');
-
- return PostModel.edit({title: 'new title'}, _.extend(context, {id: firstPost}));
- }).then(function (edited) {
- should.exist(edited);
- edited.attributes.title.should.equal('new title');
-
- done();
- }).catch(done);
- });
+ });
- it('can add, defaults are all correct', function (done) {
- var createdPostUpdatedDate,
- newPost = testUtils.DataGenerator.forModel.posts[2],
- newPostDB = testUtils.DataGenerator.Content.posts[2];
+ it('can add, defaults are all correct', function (done) {
+ var createdPostUpdatedDate,
+ newPost = testUtils.DataGenerator.forModel.posts[2],
+ newPostDB = testUtils.DataGenerator.Content.posts[2];
- PostModel.add(newPost, context).then(function (createdPost) {
- return new PostModel({id: createdPost.id}).fetch();
- }).then(function (createdPost) {
- should.exist(createdPost);
- createdPost.has('uuid').should.equal(true);
- createdPost.get('status').should.equal('draft');
- createdPost.get('title').should.equal(newPost.title, 'title is correct');
- createdPost.get('markdown').should.equal(newPost.markdown, 'markdown is correct');
- createdPost.has('html').should.equal(true);
- createdPost.get('html').should.equal(newPostDB.html);
- createdPost.get('slug').should.equal(newPostDB.slug + '-2');
- (!!createdPost.get('featured')).should.equal(false);
- (!!createdPost.get('page')).should.equal(false);
- createdPost.get('language').should.equal('en_US');
- // testing for nulls
- (createdPost.get('image') === null).should.equal(true);
- (createdPost.get('meta_title') === null).should.equal(true);
- (createdPost.get('meta_description') === null).should.equal(true);
+ PostModel.add(newPost, context).then(function (createdPost) {
+ return new PostModel({id: createdPost.id}).fetch();
+ }).then(function (createdPost) {
+ should.exist(createdPost);
+ createdPost.has('uuid').should.equal(true);
+ createdPost.get('status').should.equal('draft');
+ createdPost.get('title').should.equal(newPost.title, 'title is correct');
+ createdPost.get('markdown').should.equal(newPost.markdown, 'markdown is correct');
+ createdPost.has('html').should.equal(true);
+ createdPost.get('html').should.equal(newPostDB.html);
+ createdPost.get('slug').should.equal(newPostDB.slug + '-2');
+ (!!createdPost.get('featured')).should.equal(false);
+ (!!createdPost.get('page')).should.equal(false);
+ createdPost.get('language').should.equal('en_US');
+ // testing for nulls
+ (createdPost.get('image') === null).should.equal(true);
+ (createdPost.get('meta_title') === null).should.equal(true);
+ (createdPost.get('meta_description') === null).should.equal(true);
- createdPost.get('created_at').should.be.above(new Date(0).getTime());
- createdPost.get('created_by').should.equal(1);
- createdPost.get('author_id').should.equal(1);
- createdPost.has('author').should.equal(false);
- createdPost.get('created_by').should.equal(createdPost.get('author_id'));
- createdPost.get('updated_at').should.be.above(new Date(0).getTime());
- createdPost.get('updated_by').should.equal(1);
- should.equal(createdPost.get('published_at'), null);
- should.equal(createdPost.get('published_by'), null);
+ createdPost.get('created_at').should.be.above(new Date(0).getTime());
+ createdPost.get('created_by').should.equal(1);
+ createdPost.get('author_id').should.equal(1);
+ createdPost.has('author').should.equal(false);
+ createdPost.get('created_by').should.equal(createdPost.get('author_id'));
+ createdPost.get('updated_at').should.be.above(new Date(0).getTime());
+ createdPost.get('updated_by').should.equal(1);
+ should.equal(createdPost.get('published_at'), null);
+ should.equal(createdPost.get('published_by'), null);
- createdPostUpdatedDate = createdPost.get('updated_at');
+ createdPostUpdatedDate = createdPost.get('updated_at');
- // Set the status to published to check that `published_at` is set.
- return createdPost.save({status: 'published'}, context);
- }).then(function (publishedPost) {
- publishedPost.get('published_at').should.be.instanceOf(Date);
- publishedPost.get('published_by').should.equal(1);
- publishedPost.get('updated_at').should.be.instanceOf(Date);
- publishedPost.get('updated_by').should.equal(1);
- publishedPost.get('updated_at').should.not.equal(createdPostUpdatedDate);
+ // Set the status to published to check that `published_at` is set.
+ return createdPost.save({status: 'published'}, context);
+ }).then(function (publishedPost) {
+ publishedPost.get('published_at').should.be.instanceOf(Date);
+ publishedPost.get('published_by').should.equal(1);
+ publishedPost.get('updated_at').should.be.instanceOf(Date);
+ publishedPost.get('updated_by').should.equal(1);
+ publishedPost.get('updated_at').should.not.equal(createdPostUpdatedDate);
- done();
- }).catch(done);
+ done();
+ }).catch(done);
- });
+ });
- it('can add, with previous published_at date', function (done) {
- var previousPublishedAtDate = new Date(2013, 8, 21, 12);
+ it('can add, with previous published_at date', function (done) {
+ var previousPublishedAtDate = new Date(2013, 8, 21, 12);
- PostModel.add({
- status: 'published',
- published_at: previousPublishedAtDate,
- title: 'published_at test',
- markdown: 'This is some content'
- }, context).then(function (newPost) {
+ PostModel.add({
+ status: 'published',
+ published_at: previousPublishedAtDate,
+ title: 'published_at test',
+ markdown: 'This is some content'
+ }, context).then(function (newPost) {
- should.exist(newPost);
- new Date(newPost.get('published_at')).getTime().should.equal(previousPublishedAtDate.getTime());
+ should.exist(newPost);
+ new Date(newPost.get('published_at')).getTime().should.equal(previousPublishedAtDate.getTime());
- done();
+ done();
- }).catch(done);
- });
+ }).catch(done);
+ });
- it('can trim title', function (done) {
- var untrimmedCreateTitle = ' test trimmed create title ',
- untrimmedUpdateTitle = ' test trimmed update title ',
- newPost = {
- title: untrimmedCreateTitle,
- markdown: 'Test Content'
+ it('can trim title', function (done) {
+ var untrimmedCreateTitle = ' test trimmed create title ',
+ untrimmedUpdateTitle = ' test trimmed update title ',
+ newPost = {
+ title: untrimmedCreateTitle,
+ markdown: 'Test Content'
+ };
+
+ PostModel.add(newPost, context).then(function (createdPost) {
+ return new PostModel({ id: createdPost.id }).fetch();
+ }).then(function (createdPost) {
+ should.exist(createdPost);
+ createdPost.get('title').should.equal(untrimmedCreateTitle.trim());
+
+ return createdPost.save({ title: untrimmedUpdateTitle }, context);
+ }).then(function (updatedPost) {
+ updatedPost.get('title').should.equal(untrimmedUpdateTitle.trim());
+
+ done();
+ }).catch(done);
+ });
+
+ it('can generate a non conflicting slug', function (done) {
+ // Create 12 posts with the same title
+ sequence(_.times(12, function (i) {
+ return function () {
+ return PostModel.add({
+ title: 'Test Title',
+ markdown: 'Test Content ' + (i+1)
+ }, context);
+ };
+ })).then(function (createdPosts) {
+ // Should have created 12 posts
+ createdPosts.length.should.equal(12);
+
+ // Should have unique slugs and contents
+ _(createdPosts).each(function (post, i) {
+ var num = i + 1;
+
+ // First one has normal title
+ if (num === 1) {
+ post.get('slug').should.equal('test-title');
+ return;
+ }
+
+ post.get('slug').should.equal('test-title-' + num);
+ post.get('markdown').should.equal('Test Content ' + num);
+ });
+
+ done();
+ }).catch(done);
+ });
+
+ it('can generate slugs without duplicate hyphens', function (done) {
+ var newPost = {
+ title: 'apprehensive titles have too many spaces—and m-dashes — – and also n-dashes ',
+ markdown: 'Test Content 1'
};
- PostModel.add(newPost, context).then(function (createdPost) {
- return new PostModel({ id: createdPost.id }).fetch();
- }).then(function (createdPost) {
- should.exist(createdPost);
- createdPost.get('title').should.equal(untrimmedCreateTitle.trim());
+ PostModel.add(newPost, context).then(function (createdPost) {
- return createdPost.save({ title: untrimmedUpdateTitle }, context);
- }).then(function (updatedPost) {
- updatedPost.get('title').should.equal(untrimmedUpdateTitle.trim());
+ createdPost.get('slug').should.equal('apprehensive-titles-have-too-many-spaces-and-m-dashes-and-also-n-dashes');
- done();
- }).catch(done);
- });
+ done();
+ }).catch(done);
+ });
- it('can generate a non conflicting slug', function (done) {
- // Create 12 posts with the same title
- sequence(_.times(12, function (i) {
- return function () {
- return PostModel.add({
- title: 'Test Title',
- markdown: 'Test Content ' + (i+1)
- }, context);
+ it('can generate a safe slug when a reserved keyword is used', function(done) {
+ var newPost = {
+ title: 'rss',
+ markdown: 'Test Content 1'
};
- })).then(function (createdPosts) {
- // Should have created 12 posts
- createdPosts.length.should.equal(12);
- // Should have unique slugs and contents
- _(createdPosts).each(function (post, i) {
- var num = i + 1;
-
- // First one has normal title
- if (num === 1) {
- post.get('slug').should.equal('test-title');
- return;
- }
-
- post.get('slug').should.equal('test-title-' + num);
- post.get('markdown').should.equal('Test Content ' + num);
+ PostModel.add(newPost, context).then(function (createdPost) {
+ createdPost.get('slug').should.not.equal('rss');
+ done();
});
+ });
- done();
- }).catch(done);
- });
+ it('can generate slugs without non-ascii characters', function (done) {
+ var newPost = {
+ title: 'भुते धडकी भरवणारा आहेत',
+ markdown: 'Test Content 1'
+ };
- it('can generate slugs without duplicate hyphens', function (done) {
- var newPost = {
- title: 'apprehensive titles have too many spaces—and m-dashes — – and also n-dashes ',
- markdown: 'Test Content 1'
- };
+ PostModel.add(newPost, context).then(function (createdPost) {
+ createdPost.get('slug').should.equal('bhute-dhddkii-bhrvnnaaraa-aahet');
+ done();
+ }).catch(done);
+ });
- PostModel.add(newPost, context).then(function (createdPost) {
+ it('detects duplicate slugs before saving', function (done) {
+ var firstPost = {
+ title: 'First post',
+ markdown: 'First content 1'
+ },
+ secondPost = {
+ title: 'Second post',
+ markdown: 'Second content 1'
+ };
- createdPost.get('slug').should.equal('apprehensive-titles-have-too-many-spaces-and-m-dashes-and-also-n-dashes');
+ // Create the first post
+ PostModel.add(firstPost, context)
+ .then(function (createdFirstPost) {
+ // Store the slug for later
+ firstPost.slug = createdFirstPost.get('slug');
- done();
- }).catch(done);
- });
+ // Create the second post
+ return PostModel.add(secondPost, context);
+ }).then(function (createdSecondPost) {
+ // Store the slug for comparison later
+ secondPost.slug = createdSecondPost.get('slug');
- it('can generate a safe slug when a reserved keyword is used', function(done) {
- var newPost = {
- title: 'rss',
- markdown: 'Test Content 1'
- };
+ // Update with a conflicting slug from the first post
+ return createdSecondPost.save({
+ slug: firstPost.slug
+ }, context);
+ }).then(function (updatedSecondPost) {
- PostModel.add(newPost, context).then(function (createdPost) {
- createdPost.get('slug').should.not.equal('rss');
- done();
+ // Should have updated from original
+ updatedSecondPost.get('slug').should.not.equal(secondPost.slug);
+ // Should not have a conflicted slug from the first
+ updatedSecondPost.get('slug').should.not.equal(firstPost.slug);
+
+ return PostModel.findOne({
+ id: updatedSecondPost.id,
+ status: 'all'
+ });
+ }).then(function (foundPost) {
+
+ // Should have updated from original
+ foundPost.get('slug').should.not.equal(secondPost.slug);
+ // Should not have a conflicted slug from the first
+ foundPost.get('slug').should.not.equal(firstPost.slug);
+
+ done();
+ }).catch(done);
+ });
+
+ it('can destroy', function (done) {
+ // We're going to try deleting post id 1 which also has tag id 1
+ var firstItemData = {id: 1};
+
+ // Test that we have the post we expect, with exactly one tag
+ PostModel.findOne(firstItemData).then(function (results) {
+ var post;
+ should.exist(results);
+ post = results.toJSON();
+ post.id.should.equal(firstItemData.id);
+ post.tags.should.have.length(2);
+ post.tags[0].should.equal(firstItemData.id);
+
+ // Destroy the post
+ return PostModel.destroy(firstItemData);
+ }).then(function (response) {
+ var deleted = response.toJSON();
+
+ deleted.tags.should.be.empty;
+ should.equal(deleted.author, undefined);
+
+ // Double check we can't find the post again
+ return PostModel.findOne(firstItemData);
+ }).then(function (newResults) {
+ should.equal(newResults, null);
+
+ done();
+ }).catch(done);
+ });
+
+
+ it('can findPage, with various options', function (done) {
+ testUtils.fixtures.insertMorePosts().then(function () {
+
+ return testUtils.fixtures.insertMorePostsTags();
+ }).then(function () {
+ return PostModel.findPage({page: 2});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(2);
+ paginationResult.meta.pagination.limit.should.equal(15);
+ paginationResult.meta.pagination.pages.should.equal(4);
+ paginationResult.posts.length.should.equal(15);
+
+ return PostModel.findPage({page: 5});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(5);
+ paginationResult.meta.pagination.limit.should.equal(15);
+ paginationResult.meta.pagination.pages.should.equal(4);
+ paginationResult.posts.length.should.equal(0);
+
+ return PostModel.findPage({limit: 30});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(1);
+ paginationResult.meta.pagination.limit.should.equal(30);
+ paginationResult.meta.pagination.pages.should.equal(2);
+ paginationResult.posts.length.should.equal(30);
+
+ // Test both boolean formats
+ return PostModel.findPage({limit: 10, staticPages: true});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(1);
+ paginationResult.meta.pagination.limit.should.equal(10);
+ paginationResult.meta.pagination.pages.should.equal(1);
+ paginationResult.posts.length.should.equal(1);
+
+ // Test both boolean formats
+ return PostModel.findPage({limit: 10, staticPages: '1'});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(1);
+ paginationResult.meta.pagination.limit.should.equal(10);
+ paginationResult.meta.pagination.pages.should.equal(1);
+ paginationResult.posts.length.should.equal(1);
+
+ return PostModel.findPage({limit: 10, page: 2, status: 'all'});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.pages.should.equal(11);
+
+ done();
+ }).catch(done);
+ });
+ it('can findPage for tag, with various options', function (done) {
+ testUtils.fixtures.insertMorePosts().then(function () {
+
+ return testUtils.fixtures.insertMorePostsTags();
+ }).then(function () {
+ // Test tag filter
+ return PostModel.findPage({page: 1, tag: 'bacon'});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(1);
+ paginationResult.meta.pagination.limit.should.equal(15);
+ paginationResult.meta.pagination.pages.should.equal(1);
+ paginationResult.meta.filters.tags[0].name.should.equal('bacon');
+ paginationResult.meta.filters.tags[0].slug.should.equal('bacon');
+ paginationResult.posts.length.should.equal(2);
+
+ return PostModel.findPage({page: 1, tag: 'kitchen-sink'});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(1);
+ paginationResult.meta.pagination.limit.should.equal(15);
+ paginationResult.meta.pagination.pages.should.equal(1);
+ paginationResult.meta.filters.tags[0].name.should.equal('kitchen sink');
+ paginationResult.meta.filters.tags[0].slug.should.equal('kitchen-sink');
+ paginationResult.posts.length.should.equal(2);
+
+ return PostModel.findPage({page: 1, tag: 'injection'});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(1);
+ paginationResult.meta.pagination.limit.should.equal(15);
+ paginationResult.meta.pagination.pages.should.equal(2);
+ paginationResult.meta.filters.tags[0].name.should.equal('injection');
+ paginationResult.meta.filters.tags[0].slug.should.equal('injection');
+ paginationResult.posts.length.should.equal(15);
+
+ return PostModel.findPage({page: 2, tag: 'injection'});
+ }).then(function (paginationResult) {
+ paginationResult.meta.pagination.page.should.equal(2);
+ paginationResult.meta.pagination.limit.should.equal(15);
+ paginationResult.meta.pagination.pages.should.equal(2);
+ paginationResult.meta.filters.tags[0].name.should.equal('injection');
+ paginationResult.meta.filters.tags[0].slug.should.equal('injection');
+ paginationResult.posts.length.should.equal(10);
+
+ done();
+ }).catch(done);
});
});
- it('can generate slugs without non-ascii characters', function (done) {
- var newPost = {
- title: 'भुते धडकी भरवणारा आहेत',
- markdown: 'Test Content 1'
- };
- PostModel.add(newPost, context).then(function (createdPost) {
- createdPost.get('slug').should.equal('bhute-dhddkii-bhrvnnaaraa-aahet');
- done();
- }).catch(done);
- });
+ describe('Multiauthor Posts', function () {
+ before(testUtils.teardown);
+ afterEach(testUtils.teardown);
+ beforeEach(testUtils.setup('posts:mu'));
- it('detects duplicate slugs before saving', function (done) {
- var firstPost = {
- title: 'First post',
- markdown: 'First content 1'
- },
- secondPost = {
- title: 'Second post',
- markdown: 'Second content 1'
- };
+ should.exist(PostModel);
- // Create the first post
- PostModel.add(firstPost, context)
- .then(function (createdFirstPost) {
- // Store the slug for later
- firstPost.slug = createdFirstPost.get('slug');
+ it('can destroy multiple posts by author', function (done) {
- // Create the second post
- return PostModel.add(secondPost, context);
- }).then(function (createdSecondPost) {
- // Store the slug for comparison later
- secondPost.slug = createdSecondPost.get('slug');
-
- // Update with a conflicting slug from the first post
- return createdSecondPost.save({
- slug: firstPost.slug
- }, context);
- }).then(function (updatedSecondPost) {
-
- // Should have updated from original
- updatedSecondPost.get('slug').should.not.equal(secondPost.slug);
- // Should not have a conflicted slug from the first
- updatedSecondPost.get('slug').should.not.equal(firstPost.slug);
-
- return PostModel.findOne({
- id: updatedSecondPost.id,
- status: 'all'
- });
- }).then(function (foundPost) {
-
- // Should have updated from original
- foundPost.get('slug').should.not.equal(secondPost.slug);
- // Should not have a conflicted slug from the first
- foundPost.get('slug').should.not.equal(firstPost.slug);
+ // We're going to delete all posts by user 1
+ var authorData = {id: 1};
+ PostModel.findAll().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();
+ }).then(function (found) {
+ // Only 37 should remain
+ found.length.should.equal(37);
done();
}).catch(done);
- });
-
- it('can destroy', function (done) {
- // We're going to try deleting post id 1 which also has tag id 1
- var firstItemData = {id: 1};
-
- // Test that we have the post we expect, with exactly one tag
- PostModel.findOne(firstItemData).then(function (results) {
- var post;
- should.exist(results);
- post = results.toJSON();
- post.id.should.equal(firstItemData.id);
- post.tags.should.have.length(2);
- post.tags[0].should.equal(firstItemData.id);
-
- // Destroy the post
- return PostModel.destroy(firstItemData);
- }).then(function (response) {
- var deleted = response.toJSON();
-
- deleted.tags.should.be.empty;
- should.equal(deleted.author, undefined);
-
- // Double check we can't find the post again
- return PostModel.findOne(firstItemData);
- }).then(function (newResults) {
- should.equal(newResults, null);
-
- done();
- }).catch(done);
- });
-
- it('can findPage, with various options', function (done) {
- testUtils.fixtures.insertMorePosts().then(function () {
-
- return testUtils.fixtures.insertMorePostsTags();
- }).then(function () {
- return PostModel.findPage({page: 2});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(2);
- paginationResult.meta.pagination.limit.should.equal(15);
- paginationResult.meta.pagination.pages.should.equal(4);
- paginationResult.posts.length.should.equal(15);
-
- return PostModel.findPage({page: 5});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(5);
- paginationResult.meta.pagination.limit.should.equal(15);
- paginationResult.meta.pagination.pages.should.equal(4);
- paginationResult.posts.length.should.equal(0);
-
- return PostModel.findPage({limit: 30});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(1);
- paginationResult.meta.pagination.limit.should.equal(30);
- paginationResult.meta.pagination.pages.should.equal(2);
- paginationResult.posts.length.should.equal(30);
-
- // Test both boolean formats
- return PostModel.findPage({limit: 10, staticPages: true});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(1);
- paginationResult.meta.pagination.limit.should.equal(10);
- paginationResult.meta.pagination.pages.should.equal(1);
- paginationResult.posts.length.should.equal(1);
-
- // Test both boolean formats
- return PostModel.findPage({limit: 10, staticPages: '1'});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(1);
- paginationResult.meta.pagination.limit.should.equal(10);
- paginationResult.meta.pagination.pages.should.equal(1);
- paginationResult.posts.length.should.equal(1);
-
- return PostModel.findPage({limit: 10, page: 2, status: 'all'});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.pages.should.equal(11);
-
- done();
- }).catch(done);
- });
- it('can findPage for tag, with various options', function (done) {
- testUtils.fixtures.insertMorePosts().then(function () {
-
- return testUtils.fixtures.insertMorePostsTags();
- }).then(function () {
- // Test tag filter
- return PostModel.findPage({page: 1, tag: 'bacon'});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(1);
- paginationResult.meta.pagination.limit.should.equal(15);
- paginationResult.meta.pagination.pages.should.equal(1);
- paginationResult.meta.filters.tags[0].name.should.equal('bacon');
- paginationResult.meta.filters.tags[0].slug.should.equal('bacon');
- paginationResult.posts.length.should.equal(2);
-
- return PostModel.findPage({page: 1, tag: 'kitchen-sink'});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(1);
- paginationResult.meta.pagination.limit.should.equal(15);
- paginationResult.meta.pagination.pages.should.equal(1);
- paginationResult.meta.filters.tags[0].name.should.equal('kitchen sink');
- paginationResult.meta.filters.tags[0].slug.should.equal('kitchen-sink');
- paginationResult.posts.length.should.equal(2);
-
- return PostModel.findPage({page: 1, tag: 'injection'});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(1);
- paginationResult.meta.pagination.limit.should.equal(15);
- paginationResult.meta.pagination.pages.should.equal(2);
- paginationResult.meta.filters.tags[0].name.should.equal('injection');
- paginationResult.meta.filters.tags[0].slug.should.equal('injection');
- paginationResult.posts.length.should.equal(15);
-
- return PostModel.findPage({page: 2, tag: 'injection'});
- }).then(function (paginationResult) {
- paginationResult.meta.pagination.page.should.equal(2);
- paginationResult.meta.pagination.limit.should.equal(15);
- paginationResult.meta.pagination.pages.should.equal(2);
- paginationResult.meta.filters.tags[0].name.should.equal('injection');
- paginationResult.meta.filters.tags[0].slug.should.equal('injection');
- paginationResult.posts.length.should.equal(10);
-
- done();
- }).catch(done);
+ });
});
// disabling sanitization until we can implement a better version
diff --git a/core/test/utils/fixtures/data-generator.js b/core/test/utils/fixtures/data-generator.js
index b79e75bde6..6e233049db 100644
--- a/core/test/utils/fixtures/data-generator.js
+++ b/core/test/utils/fixtures/data-generator.js
@@ -268,14 +268,16 @@ DataGenerator.forKnex = (function () {
});
}
- function createGenericPost(uniqueInteger, status, language) {
+ function createGenericPost(uniqueInteger, status, language, author_id) {
status = status || 'draft';
language = language || 'en_US';
+ author_id = author_id || 1;
return createPost({
uuid: uuid.v4(),
title: 'Test Post ' + uniqueInteger,
slug: 'ghost-from-fiction-to-function-' + uniqueInteger,
+ author_id: author_id,
markdown: "Three days ago I released a concept page<\/a> for a lite version of WordPress that I've been thinking about for a long time, called Ghost. I think it's fair to say that I didn't quite anticipate how strong the reaction would be - and I've hardly had time to catch my breath in the last 72 hours.\n\nThe response was overwhelming, and overwhelmingly positive. In the first 6 hours my site got 35,000 page views after hitting the number 1 slot on Hacker News<\/a>. As of right now, the traffic count is just over 91,000 page views<\/a> - and Ghost has been featured all over the place. Notable mentions so far include Christina Warren from Mashable, who wrote about it<\/a>. Michael Carney from PandoDaily interviewed me about it<\/a>. Someone even wrote about it in Chinese<\/a>. That's pretty cool.\n\n\nThe feedback has been amazing, and while it's impossible to reply to all of the messages individually, I'm getting to as many of them as I can and I want to thank each and every one of you who took the time to send me a message or share the concept because you liked it. Now that the initial storm has died down a bit, I wanted to take some time to answer some of the more common questions and talk about what's next.\n