Added unpublish bulk action to posts
fixes https://github.com/TryGhost/Team/issues/2994
This commit is contained in:
parent
d4e9c23f84
commit
9eb886d26d
@ -1,9 +1,12 @@
|
||||
<ul class="gh-posts-context-menu dropdown-menu dropdown-triangle-top-left">
|
||||
<li>
|
||||
<button class="mr2" type="button" disabled {{on "click" @menu.close}}>
|
||||
<span>Unpublish</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
{{#if this.canUnpublishSelection}}
|
||||
<li>
|
||||
<button class="mr2" type="button" {{on "click" this.unpublishPosts}}>
|
||||
<span>Unpublish</span>
|
||||
</button>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if this.canFeatureSelection}}
|
||||
{{#if this.shouldFeatureSelection }}
|
||||
<li>
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Component from '@glimmer/component';
|
||||
import DeletePostsModal from './modals/delete-posts';
|
||||
import EditPostsAccessModal from './modals/edit-posts-access';
|
||||
import UnpublishPostsModal from './modals/unpublish-posts';
|
||||
import nql from '@tryghost/nql';
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency';
|
||||
@ -31,6 +33,16 @@ export default class PostsContextMenu extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
async unpublishPosts() {
|
||||
this.menu.close();
|
||||
await this.modals.open(UnpublishPostsModal, {
|
||||
isSingle: this.selectionList.isSingle,
|
||||
count: this.selectionList.count,
|
||||
confirm: this.unpublishPostsTask
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
async editPostsAccess() {
|
||||
this.menu.close();
|
||||
@ -56,6 +68,50 @@ export default class PostsContextMenu extends Component {
|
||||
return true;
|
||||
}
|
||||
|
||||
@task
|
||||
*unpublishPostsTask(close) {
|
||||
const updatedModels = this.selectionList.availableModels;
|
||||
yield this.performBulkEdit('unpublish');
|
||||
|
||||
// Update the models on the client side
|
||||
for (const post of updatedModels) {
|
||||
if (post.status === 'published' || post.status === 'sent') {
|
||||
// We need to do it this way to prevent marking the model as dirty
|
||||
this.store.push({
|
||||
data: {
|
||||
id: post.id,
|
||||
type: 'post',
|
||||
attributes: {
|
||||
status: 'draft'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Remove posts that no longer match the filter
|
||||
this.updateFilteredPosts();
|
||||
|
||||
close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
updateFilteredPosts() {
|
||||
const updatedModels = this.selectionList.availableModels;
|
||||
const filter = this.selectionList.allFilter;
|
||||
const filterNql = nql(filter);
|
||||
|
||||
const remainingModels = this.selectionList.infinityModel.content.filter((model) => {
|
||||
if (!updatedModels.find(u => u.id === model.id)) {
|
||||
return true;
|
||||
}
|
||||
return filterNql.queryJSON(model);
|
||||
});
|
||||
// Deleteobjects method from infintiymodel is broken for all models except the first page, so we cannot use this
|
||||
this.infinity.replace(this.selectionList.infinityModel, remainingModels);
|
||||
}
|
||||
|
||||
@task
|
||||
*editPostsAccessTask(close, {visibility, tiers}) {
|
||||
const updatedModels = this.selectionList.availableModels;
|
||||
@ -80,6 +136,9 @@ export default class PostsContextMenu extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
// Remove posts that no longer match the filter
|
||||
this.updateFilteredPosts();
|
||||
|
||||
close();
|
||||
|
||||
return true;
|
||||
@ -116,7 +175,16 @@ export default class PostsContextMenu extends Component {
|
||||
if (!this.selectionList.isSingle) {
|
||||
return true;
|
||||
}
|
||||
return this.selectionList.availableModels[0].get('status') !== 'sent';
|
||||
return this.selectionList.availableModels[0]?.get('status') !== 'sent';
|
||||
}
|
||||
|
||||
get canUnpublishSelection() {
|
||||
for (const m of this.selectionList.availableModels) {
|
||||
if (['published', 'sent'].includes(m.status)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@action
|
||||
@ -138,6 +206,9 @@ export default class PostsContextMenu extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
// Remove posts that no longer match the filter
|
||||
this.updateFilteredPosts();
|
||||
|
||||
// Close the menu
|
||||
this.menu.close();
|
||||
}
|
||||
@ -161,6 +232,9 @@ export default class PostsContextMenu extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
// Remove posts that no longer match the filter
|
||||
this.updateFilteredPosts();
|
||||
|
||||
// Close the menu
|
||||
this.menu.close();
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
<div class="modal-content" data-test-modal="unpublish-posts">
|
||||
<header class="modal-header">
|
||||
<h1>Are you sure you want to unpublish {{if @data.isSingle 'this post' (concat @data.count ' posts')}}?</h1>
|
||||
</header>
|
||||
<button type="button" class="close" title="Close" {{on "click" (fn @close false)}} data-test-button="close">{{svg-jar "close"}}<span class="hidden">Close</span></button>
|
||||
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
You're about to unpublish {{if @data.isSingle 'this post' (concat @data.count ' posts')}}.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="gh-btn" data-test-button="cancel" type="button" {{on "click" (fn @close false)}}><span>Cancel</span></button>
|
||||
|
||||
<GhTaskButton
|
||||
@buttonText="Unpublish"
|
||||
@runningText="Unpublishing"
|
||||
@showSuccess={{false}}
|
||||
@task={{@data.confirm}}
|
||||
@taskArgs={{@close}}
|
||||
@class="gh-btn gh-btn-red gh-btn-icon"
|
||||
data-test-button="confirm"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -63,7 +63,14 @@ class PostsService {
|
||||
return model;
|
||||
}
|
||||
|
||||
#mergeFilters(...filters) {
|
||||
return filters.filter(filter => filter).map(f => `(${f})`).join('+');
|
||||
}
|
||||
|
||||
async bulkEdit(data, options) {
|
||||
if (data.action === 'unpublish') {
|
||||
return await this.#updatePosts({status: 'draft'}, {filter: this.#mergeFilters('status:[published,sent]', options.filter)});
|
||||
}
|
||||
if (data.action === 'feature') {
|
||||
return await this.#updatePosts({featured: true}, {filter: options.filter});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user