mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 05:37:34 +03:00
Fixed collection cards not re-rendering when posts were bulk-edited
no issue - bulk edit actions bypass the Bookshelf model hooks which meant our page reset behaviour in `onSaving` and `onDestroyed` was not being hit - added overrides to `bulkEdit` and `bulkDestroy` to add the same page-reset behaviour any time we have a bulk edit or destroy
This commit is contained in:
parent
6e46e8b5ba
commit
d541991046
@ -1367,6 +1367,24 @@ Post = ghostBookshelf.Model.extend({
|
||||
return editPost();
|
||||
},
|
||||
|
||||
bulkEdit: async function bulkEdit(ids, tableName, options) {
|
||||
if (tableName === this.prototype.tableName) {
|
||||
const result = await ghostBookshelf.Model.bulkEdit.call(this, ids, tableName, options);
|
||||
|
||||
if (labs.isSet('collectionsCard')) {
|
||||
// reset all page HTML so collection cards can be re-rendered with updated posts
|
||||
// NOTE: we can't check for only published edits here as we don't have access to previous values
|
||||
// to see if a previously published post has been unpublished, so we just reset all pages
|
||||
const pageResetQuery = ghostBookshelf.knex.raw('UPDATE posts set html = NULL WHERE type = "page" AND lexical IS NOT NULL');
|
||||
await (options.transacting ? pageResetQuery.transacting(options.transacting) : pageResetQuery);
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return ghostBookshelf.Model.bulkEdit.call(this, ids, tableName, options);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ### Add
|
||||
* @extends ghostBookshelf.Model.add to handle returning the full object
|
||||
@ -1413,6 +1431,34 @@ Post = ghostBookshelf.Model.extend({
|
||||
return destroyPost();
|
||||
},
|
||||
|
||||
bulkDestroy: async function bulkDestroy(ids, tableName, options) {
|
||||
if (tableName === this.prototype.tableName) {
|
||||
if (labs.isSet('collectionsCard')) {
|
||||
// get count of published posts to be destroyed before they no longer exist to count
|
||||
const deletedPublishedCount = await this.query((qb) => {
|
||||
qb.where('type', 'post')
|
||||
.where('status', 'published')
|
||||
.whereIn('id', ids);
|
||||
}).count({transacting: options.transacting});
|
||||
|
||||
const result = await ghostBookshelf.Model.bulkDestroy.call(this, ids, tableName, options);
|
||||
|
||||
// if we've deleted any published posts, we need to reset the html for all pages so dynamic collection
|
||||
// card content can be re-rendered
|
||||
if (deletedPublishedCount > 0) {
|
||||
const pageResetQuery = ghostBookshelf.knex.raw('UPDATE posts set html = NULL WHERE type = "page" AND lexical IS NOT NULL');
|
||||
await (options.transacting ? pageResetQuery.transacting(options.transacting) : pageResetQuery);
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return ghostBookshelf.Model.bulkDestroy.call(this, ids, tableName, options);
|
||||
}
|
||||
} else {
|
||||
return ghostBookshelf.Model.bulkDestroy.call(this, ids, tableName, options);
|
||||
}
|
||||
},
|
||||
|
||||
// NOTE: the `authors` extension is the parent of the post model. It also has a permissible function.
|
||||
permissible: async function permissible(postModel, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasApiKeyPermission) {
|
||||
let isContributor;
|
||||
|
@ -1,4 +1,6 @@
|
||||
const should = require('should');
|
||||
const DomainEvents = require('@tryghost/domain-events');
|
||||
const {mobiledocToLexical} = require('@tryghost/kg-converters');
|
||||
const {agentProvider, fixtureManager, mockManager} = require('../../utils/e2e-framework');
|
||||
const models = require('../../../core/server/models');
|
||||
const assert = require('assert/strict');
|
||||
@ -8,15 +10,26 @@ describe('Posts Bulk API', function () {
|
||||
|
||||
before(async function () {
|
||||
mockManager.mockLabsEnabled('collections');
|
||||
mockManager.mockLabsEnabled('collectionsCard');
|
||||
|
||||
agent = await agentProvider.getAdminAPIAgent();
|
||||
|
||||
// Note that we generate lots of fixtures here to test the bulk deletion correctly
|
||||
await fixtureManager.init('posts', 'newsletters', 'members:newsletters', 'emails', 'redirects', 'clicks', 'comments', 'feedback', 'links', 'mentions');
|
||||
await agent.loginAsOwner();
|
||||
|
||||
// convert inserted pages to lexical so we can test page.html reset/re-render
|
||||
const pages = await models.Post.where('type', 'page').fetchAll();
|
||||
for (const page of pages) {
|
||||
const lexical = mobiledocToLexical(page.get('mobiledoc'));
|
||||
await models.Base.knex.raw('UPDATE posts SET mobiledoc=NULL, lexical=? where id=?', [lexical, page.id]);
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
afterEach(async function () {
|
||||
// give pages some HTML back to alleviate test interdependence when pages are reset on create/update/delete
|
||||
await models.Base.knex.raw('UPDATE posts SET html = "<p>Testing</p>" WHERE type = \'page\' AND html IS NULL');
|
||||
|
||||
mockManager.restore();
|
||||
});
|
||||
|
||||
@ -48,6 +61,13 @@ describe('Posts Bulk API', function () {
|
||||
|
||||
assert.equal(response.body.bulk.meta.stats.successful, amount, `Expect all matching posts (${amount}) to be changed`);
|
||||
|
||||
// Check page HTML was reset to enable re-render of collection cards
|
||||
// Must be done before fetch all posts otherwise they will be re-rendered
|
||||
const totalPageCount = await models.Post.where({type: 'page'}).count();
|
||||
const emptyPageCount = await models.Post.where({html: null, type: 'page'}).count();
|
||||
should.exist(emptyPageCount);
|
||||
emptyPageCount.should.equal(totalPageCount, 'no. of render-queued pages after bulk edit');
|
||||
|
||||
// Fetch all posts and check if they are featured
|
||||
const posts = await models.Post.findAll({filter, status: 'all'});
|
||||
assert.equal(posts.length, amount, `Expect all matching posts (${amount}) to be changed`);
|
||||
@ -281,7 +301,7 @@ describe('Posts Bulk API', function () {
|
||||
|
||||
it('Can unpublish posts', async function () {
|
||||
const filter = 'status:[published]';
|
||||
const changedPosts = await models.Post.findPage({filter, limit: 1, status: 'all'});
|
||||
const changedPosts = await models.Post.findPage({filter, status: 'published'});
|
||||
const amount = changedPosts.meta.pagination.total;
|
||||
|
||||
assert(amount > 0, 'Expect at least one post to be affected for this test to work');
|
||||
@ -301,6 +321,10 @@ describe('Posts Bulk API', function () {
|
||||
// Fetch all posts and check if they are unpublished
|
||||
const posts = await models.Post.findAll({filter, status: 'all'});
|
||||
assert.equal(posts.length, 0, `Expect all matching posts (${amount}) to be unpublished`);
|
||||
|
||||
// Re-publish the posts so we don't affect later tests
|
||||
const postIds = changedPosts.data.map(post => post.id);
|
||||
await models.Base.knex.raw(`UPDATE posts SET status = \'published\' WHERE id IN (${postIds.map(() => '?').join(',')})`, [...postIds]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -332,6 +356,12 @@ describe('Posts Bulk API', function () {
|
||||
// Check if all posts were deleted
|
||||
const posts = await models.Post.findPage({filter, status: 'all'});
|
||||
assert.equal(posts.meta.pagination.total, 0, `Expect all matching posts (${amount}) to be deleted`);
|
||||
|
||||
// Check page HTML was reset to enable re-render of collection cards
|
||||
const totalPageCount = await models.Post.where({type: 'page'}).count();
|
||||
const emptyPageCount = await models.Post.where({html: null, type: 'page'}).count();
|
||||
should.exist(emptyPageCount);
|
||||
emptyPageCount.should.equal(totalPageCount, 'no. of render-queued pages after bulk delete');
|
||||
});
|
||||
|
||||
it('Can delete all posts', async function () {
|
||||
|
Loading…
Reference in New Issue
Block a user