mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-30 21:40:39 +03:00
Added support for creating new tags when bulk editing posts
refs https://github.com/TryGhost/Team/issues/2922
This commit is contained in:
parent
76fae2a724
commit
788aa34c8b
@ -105,10 +105,49 @@ export default class PostsContextMenu extends Component {
|
|||||||
*addTagToPostsTask(tags) {
|
*addTagToPostsTask(tags) {
|
||||||
const updatedModels = this.selectionList.availableModels;
|
const updatedModels = this.selectionList.availableModels;
|
||||||
|
|
||||||
yield this.performBulkEdit('addTag', {tags: tags.map(tag => tag.id)});
|
yield this.performBulkEdit('addTag', {
|
||||||
|
tags: tags.map((t) => {
|
||||||
|
return {
|
||||||
|
id: t.id,
|
||||||
|
name: t.name,
|
||||||
|
slug: t.slug
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
this.notifications.showNotification(this.#getToastMessage('tagsAdded'), {type: 'success'});
|
this.notifications.showNotification(this.#getToastMessage('tagsAdded'), {type: 'success'});
|
||||||
|
|
||||||
|
const serializedTags = tags.toArray().map((t) => {
|
||||||
|
return {
|
||||||
|
...t.serialize({includeId: true}),
|
||||||
|
type: 'tag'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Destroy unsaved new tags (otherwise we could select them again)
|
||||||
|
this.store.peekAll('tag').forEach((tag) => {
|
||||||
|
if (tag.isNew) {
|
||||||
|
tag.destroyRecord();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// For new tags, attach the id to it, so we can link the new tag to the post
|
||||||
|
let allTags = null;
|
||||||
|
|
||||||
|
for (const tag of serializedTags) {
|
||||||
|
if (!tag.id) {
|
||||||
|
if (!allTags) {
|
||||||
|
// Update tags on the client side (we could have created new tags)
|
||||||
|
yield this.store.query('tag', {limit: 'all'});
|
||||||
|
allTags = this.store.peekAll('tag').toArray();
|
||||||
|
}
|
||||||
|
const createdTag = allTags.find(t => t.name === tag.name && t.id);
|
||||||
|
if (createdTag) {
|
||||||
|
tag.id = createdTag.id;
|
||||||
|
tag.slug = createdTag.slug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the models on the client side
|
// Update the models on the client side
|
||||||
for (const post of updatedModels) {
|
for (const post of updatedModels) {
|
||||||
const newTags = post.tags.toArray().map((t) => {
|
const newTags = post.tags.toArray().map((t) => {
|
||||||
@ -117,12 +156,9 @@ export default class PostsContextMenu extends Component {
|
|||||||
type: 'tag'
|
type: 'tag'
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
for (const tag of tags) {
|
for (const tag of serializedTags) {
|
||||||
if (!newTags.find(t => t.id === tag.id)) {
|
if (!newTags.find(t => t.id === tag.id)) {
|
||||||
newTags.push({
|
newTags.push(tag);
|
||||||
...tag.serialize({includeId: true}),
|
|
||||||
type: 'tag'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,13 @@ export default class AddTag extends Component {
|
|||||||
// store and be updated when the above query returns
|
// store and be updated when the above query returns
|
||||||
this.store.query('tag', {limit: 'all'});
|
this.store.query('tag', {limit: 'all'});
|
||||||
this.#availableTags = this.store.peekAll('tag');
|
this.#availableTags = this.store.peekAll('tag');
|
||||||
|
|
||||||
|
// Destroy unsaved new tags (otherwise we could select them again -> create them again)
|
||||||
|
this.#availableTags.forEach((tag) => {
|
||||||
|
if (tag.isNew) {
|
||||||
|
tag.destroyRecord();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@ -58,11 +65,8 @@ export default class AddTag extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
shouldAllowCreate() {
|
shouldAllowCreate(nameInput) {
|
||||||
return false;
|
return !this.#findTagByName(nameInput.trim(), this.#availableTags);
|
||||||
|
|
||||||
// This is not supported by the backend yet
|
|
||||||
// return !this.#findTagByName(nameInput.trim(), this.#availableTags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#findTagByName(name, tags) {
|
#findTagByName(name, tags) {
|
||||||
|
@ -8,6 +8,7 @@ const messages = {
|
|||||||
invalidVisibilityFilter: 'Invalid visibility filter.',
|
invalidVisibilityFilter: 'Invalid visibility filter.',
|
||||||
invalidVisibility: 'Invalid visibility value.',
|
invalidVisibility: 'Invalid visibility value.',
|
||||||
invalidTiers: 'Invalid tiers value.',
|
invalidTiers: 'Invalid tiers value.',
|
||||||
|
invalidTags: 'Invalid tags value.',
|
||||||
invalidEmailSegment: 'The email segment parameter doesn\'t contain a valid filter',
|
invalidEmailSegment: 'The email segment parameter doesn\'t contain a valid filter',
|
||||||
unsupportedBulkAction: 'Unsupported bulk action'
|
unsupportedBulkAction: 'Unsupported bulk action'
|
||||||
};
|
};
|
||||||
@ -95,6 +96,23 @@ class PostsService {
|
|||||||
return await this.#updatePosts({visibility: data.meta.visibility, tiers}, {filter: options.filter});
|
return await this.#updatePosts({visibility: data.meta.visibility, tiers}, {filter: options.filter});
|
||||||
}
|
}
|
||||||
if (data.action === 'addTag') {
|
if (data.action === 'addTag') {
|
||||||
|
if (!Array.isArray(data.meta.tags)) {
|
||||||
|
throw new errors.IncorrectUsageError({
|
||||||
|
message: tpl(messages.invalidTags)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const tag of data.meta.tags) {
|
||||||
|
if (typeof tag !== 'object') {
|
||||||
|
throw new errors.IncorrectUsageError({
|
||||||
|
message: tpl(messages.invalidTags)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!tag.id && !tag.name) {
|
||||||
|
throw new errors.IncorrectUsageError({
|
||||||
|
message: tpl(messages.invalidTags)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return await this.#bulkAddTags({tags: data.meta.tags}, {filter: options.filter});
|
return await this.#bulkAddTags({tags: data.meta.tags}, {filter: options.filter});
|
||||||
}
|
}
|
||||||
throw new errors.IncorrectUsageError({
|
throw new errors.IncorrectUsageError({
|
||||||
@ -118,17 +136,25 @@ class PostsService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create tags that don't exist
|
||||||
|
for (const tag of data.tags) {
|
||||||
|
if (!tag.id) {
|
||||||
|
const createdTag = await this.models.Tag.add(tag, {transacting: options.transacting});
|
||||||
|
tag.id = createdTag.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const postRows = await this.models.Post.getFilteredCollectionQuery({
|
const postRows = await this.models.Post.getFilteredCollectionQuery({
|
||||||
filter: options.filter,
|
filter: options.filter,
|
||||||
status: 'all'
|
status: 'all'
|
||||||
}).select('posts.id');
|
}).select('posts.id');
|
||||||
|
|
||||||
const postTags = data.tags.reduce((pt, tagId) => {
|
const postTags = data.tags.reduce((pt, tag) => {
|
||||||
return pt.concat(postRows.map((post) => {
|
return pt.concat(postRows.map((post) => {
|
||||||
return {
|
return {
|
||||||
id: (new ObjectId()).toHexString(),
|
id: (new ObjectId()).toHexString(),
|
||||||
post_id: post.id,
|
post_id: post.id,
|
||||||
tag_id: tagId,
|
tag_id: tag.id,
|
||||||
sort_order: 0
|
sort_order: 0
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
Loading…
Reference in New Issue
Block a user