mirror of
https://github.com/strapi/strapi.git
synced 2025-09-22 14:59:07 +00:00
Add bulk publish and unpublish
This commit is contained in:
parent
1347bb4f26
commit
119b88a1b1
@ -240,10 +240,10 @@ const BulkActionsBar = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirmUnpublishAll = async () => {
|
const handleConfirmUnpublishAll = () => {
|
||||||
try {
|
try {
|
||||||
setIsConfirmButtonLoading(true);
|
setIsConfirmButtonLoading(true);
|
||||||
await onConfirmUnpublishAll(selectedEntries);
|
onConfirmUnpublishAll(selectedEntries);
|
||||||
clearSelectedEntries();
|
clearSelectedEntries();
|
||||||
setIsConfirmButtonLoading(false);
|
setIsConfirmButtonLoading(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -3,7 +3,7 @@ import { act, render, screen, within } from '@testing-library/react';
|
|||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
import BulkActionsBar from '../index';
|
import BulkActionsBar from '../index'
|
||||||
|
|
||||||
jest.mock('@strapi/helper-plugin', () => ({
|
jest.mock('@strapi/helper-plugin', () => ({
|
||||||
...jest.requireActual('@strapi/helper-plugin'),
|
...jest.requireActual('@strapi/helper-plugin'),
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
} from '@strapi/design-system';
|
} from '@strapi/design-system';
|
||||||
|
|
||||||
import { ArrowLeft, Plus, Cog } from '@strapi/icons';
|
import { ArrowLeft, Plus, Cog } from '@strapi/icons';
|
||||||
|
import { useMutation } from 'react-query';
|
||||||
|
|
||||||
import DynamicTable from '../../components/DynamicTable';
|
import DynamicTable from '../../components/DynamicTable';
|
||||||
import AttributeFilter from '../../components/AttributeFilter';
|
import AttributeFilter from '../../components/AttributeFilter';
|
||||||
@ -102,6 +103,14 @@ function ListView({
|
|||||||
const fetchClient = useFetchClient();
|
const fetchClient = useFetchClient();
|
||||||
const { post, del } = fetchClient;
|
const { post, del } = fetchClient;
|
||||||
|
|
||||||
|
const bulkAction = async ({ query, input }) => {
|
||||||
|
const { data } = await post(query, { ...input });
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const bulkPublishMutation = useMutation(bulkAction);
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
// Using a ref to avoid requests being fired multiple times on slug on change
|
// Using a ref to avoid requests being fired multiple times on slug on change
|
||||||
// We need it because the hook as mulitple dependencies so it may run before the permissions have checked
|
// We need it because the hook as mulitple dependencies so it may run before the permissions have checked
|
||||||
@ -180,21 +189,6 @@ function ListView({
|
|||||||
[fetchData, params, slug, toggleNotification, formatAPIError, post]
|
[fetchData, params, slug, toggleNotification, formatAPIError, post]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleConfirmPublishAllData = async (selectedEntries) => {
|
|
||||||
const validations = await validateEntriesToPublish(selectedEntries);
|
|
||||||
console.log('Validations', validations);
|
|
||||||
|
|
||||||
if (validations.errors.length > 0) {
|
|
||||||
// TODO make a request to the API and refetch the data
|
|
||||||
console.info('Publishing all data', selectedEntries);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleConfirmUnpublishAllData = (ids) => {
|
|
||||||
// TODO make a request to the API and refetch the data
|
|
||||||
console.info('Unpublishing all data', ids);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleConfirmDeleteData = useCallback(
|
const handleConfirmDeleteData = useCallback(
|
||||||
async (idToDelete) => {
|
async (idToDelete) => {
|
||||||
try {
|
try {
|
||||||
@ -254,6 +248,76 @@ function ListView({
|
|||||||
return validations;
|
return validations;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleConfirmPublishAllData = async (selectedEntries) => {
|
||||||
|
const validations = await validateEntriesToPublish(selectedEntries);
|
||||||
|
|
||||||
|
if (Object.values(validations.errors).length) {
|
||||||
|
toggleNotification({
|
||||||
|
type: 'warning',
|
||||||
|
title: {
|
||||||
|
id: 'content-manager.listView.validation.errors.title',
|
||||||
|
defaultMessage: 'Action required',
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
id: 'content-manager.listView.validation.errors.message',
|
||||||
|
defaultMessage:
|
||||||
|
'Please make sure all fields are valid before publishing (required field, min/max character limit, etc.)',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkPublishMutation.mutate(
|
||||||
|
{
|
||||||
|
query: `/content-manager/collection-types/${contentType.uid}/actions/bulkPublish`,
|
||||||
|
input: { ids: selectedEntries },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess() {
|
||||||
|
fetchData(`/content-manager/collection-types/${slug}${params}`);
|
||||||
|
toggleNotification({
|
||||||
|
type: 'success',
|
||||||
|
message: { id: 'content-manager.success.record.publish', defaultMessage: 'Published' },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
toggleNotification({
|
||||||
|
type: 'warning',
|
||||||
|
message: formatAPIError(error),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmUnpublishAllData = (selectedEntries) => {
|
||||||
|
bulkPublishMutation.mutate(
|
||||||
|
{
|
||||||
|
query: `/content-manager/collection-types/${contentType.uid}/actions/bulkUnpublish`,
|
||||||
|
input: { ids: selectedEntries },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess() {
|
||||||
|
fetchData(`/content-manager/collection-types/${slug}${params}`);
|
||||||
|
toggleNotification({
|
||||||
|
type: 'success',
|
||||||
|
message: {
|
||||||
|
id: 'content-manager.success.record.unpublish',
|
||||||
|
defaultMessage: 'Unpublished',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
toggleNotification({
|
||||||
|
type: 'warning',
|
||||||
|
message: formatAPIError(error),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const CancelToken = axios.CancelToken;
|
const CancelToken = axios.CancelToken;
|
||||||
const source = CancelToken.source();
|
const source = CancelToken.source();
|
||||||
@ -417,6 +481,7 @@ ListView.propTypes = {
|
|||||||
layout: PropTypes.exact({
|
layout: PropTypes.exact({
|
||||||
components: PropTypes.object.isRequired,
|
components: PropTypes.object.isRequired,
|
||||||
contentType: PropTypes.shape({
|
contentType: PropTypes.shape({
|
||||||
|
uid: PropTypes.string.isRequired,
|
||||||
attributes: PropTypes.object.isRequired,
|
attributes: PropTypes.object.isRequired,
|
||||||
metadatas: PropTypes.object.isRequired,
|
metadatas: PropTypes.object.isRequired,
|
||||||
info: PropTypes.shape({ displayName: PropTypes.string.isRequired }).isRequired,
|
info: PropTypes.shape({ displayName: PropTypes.string.isRequired }).isRequired,
|
||||||
|
@ -817,6 +817,8 @@
|
|||||||
"content-manager.success.record.save": "Saved",
|
"content-manager.success.record.save": "Saved",
|
||||||
"content-manager.success.record.unpublish": "Unpublished",
|
"content-manager.success.record.unpublish": "Unpublished",
|
||||||
"content-manager.utils.data-loaded": "The {number, plural, =1 {entry has} other {entries have}} successfully been loaded",
|
"content-manager.utils.data-loaded": "The {number, plural, =1 {entry has} other {entries have}} successfully been loaded",
|
||||||
|
"content-manager.listView.validation.errors.title": "Action required",
|
||||||
|
"content-manager.listView.validation.errors.message": "Please make sure all fields are valid before publishing (required field, min/max character limit, etc.)",
|
||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
"form.button.continue": "Continue",
|
"form.button.continue": "Continue",
|
||||||
"form.button.done": "Done",
|
"form.button.done": "Done",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const { setCreatorFields, pipeAsync } = require('@strapi/utils');
|
const { setCreatorFields, pipeAsync } = require('@strapi/utils');
|
||||||
|
|
||||||
const { getService, pickWritableAttributes } = require('../utils');
|
const { getService, pickWritableAttributes } = require('../utils');
|
||||||
const { validateBulkDeleteInput } = require('./validation');
|
const { validateBulkActionInput } = require('./validation');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
async find(ctx) {
|
async find(ctx) {
|
||||||
@ -181,6 +181,64 @@ module.exports = {
|
|||||||
ctx.body = await permissionChecker.sanitizeOutput(result);
|
ctx.body = await permissionChecker.sanitizeOutput(result);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async bulkPublish(ctx) {
|
||||||
|
const { userAbility } = ctx.state;
|
||||||
|
const { model } = ctx.params;
|
||||||
|
const { query, body } = ctx.request;
|
||||||
|
const { ids } = body;
|
||||||
|
|
||||||
|
await validateBulkActionInput(body);
|
||||||
|
|
||||||
|
const entityManager = getService('entity-manager');
|
||||||
|
const permissionChecker = getService('permission-checker').create({ userAbility, model });
|
||||||
|
|
||||||
|
if (permissionChecker.cannot.publish()) {
|
||||||
|
return ctx.forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionQuery = await permissionChecker.sanitizedQuery.publish(query);
|
||||||
|
|
||||||
|
const idsWhereClause = { id: { $in: ids } };
|
||||||
|
const params = {
|
||||||
|
...permissionQuery,
|
||||||
|
filters: {
|
||||||
|
$and: [idsWhereClause].concat(permissionQuery.filters || []),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { count } = await entityManager.publishMany(params, model);
|
||||||
|
ctx.body = { count };
|
||||||
|
},
|
||||||
|
|
||||||
|
async bulkUnpublish(ctx) {
|
||||||
|
const { userAbility } = ctx.state;
|
||||||
|
const { model } = ctx.params;
|
||||||
|
const { query, body } = ctx.request;
|
||||||
|
const { ids } = body;
|
||||||
|
|
||||||
|
await validateBulkActionInput(body);
|
||||||
|
|
||||||
|
const entityManager = getService('entity-manager');
|
||||||
|
const permissionChecker = getService('permission-checker').create({ userAbility, model });
|
||||||
|
|
||||||
|
if (permissionChecker.cannot.unpublish()) {
|
||||||
|
return ctx.forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissionQuery = await permissionChecker.sanitizedQuery.unpublish(query);
|
||||||
|
|
||||||
|
const idsWhereClause = { id: { $in: ids } };
|
||||||
|
const params = {
|
||||||
|
...permissionQuery,
|
||||||
|
filters: {
|
||||||
|
$and: [idsWhereClause].concat(permissionQuery.filters || []),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { count } = await entityManager.unpublishMany(params, model);
|
||||||
|
ctx.body = { count };
|
||||||
|
},
|
||||||
|
|
||||||
async unpublish(ctx) {
|
async unpublish(ctx) {
|
||||||
const { userAbility, user } = ctx.state;
|
const { userAbility, user } = ctx.state;
|
||||||
const { id, model } = ctx.params;
|
const { id, model } = ctx.params;
|
||||||
@ -217,7 +275,7 @@ module.exports = {
|
|||||||
const { query, body } = ctx.request;
|
const { query, body } = ctx.request;
|
||||||
const { ids } = body;
|
const { ids } = body;
|
||||||
|
|
||||||
await validateBulkDeleteInput(body);
|
await validateBulkActionInput(body);
|
||||||
|
|
||||||
const entityManager = getService('entity-manager');
|
const entityManager = getService('entity-manager');
|
||||||
const permissionChecker = getService('permission-checker').create({ userAbility, model });
|
const permissionChecker = getService('permission-checker').create({ userAbility, model });
|
||||||
|
@ -13,7 +13,7 @@ const TYPES = ['singleType', 'collectionType'];
|
|||||||
*/
|
*/
|
||||||
const kindSchema = yup.string().oneOf(TYPES).nullable();
|
const kindSchema = yup.string().oneOf(TYPES).nullable();
|
||||||
|
|
||||||
const bulkDeleteInputSchema = yup
|
const bulkActionInputSchema = yup
|
||||||
.object({
|
.object({
|
||||||
ids: yup.array().of(yup.strapiID()).min(1).required(),
|
ids: yup.array().of(yup.strapiID()).min(1).required(),
|
||||||
})
|
})
|
||||||
@ -64,7 +64,7 @@ const validatePagination = ({ page, pageSize }) => {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
createModelConfigurationSchema,
|
createModelConfigurationSchema,
|
||||||
validateKind: validateYupSchema(kindSchema),
|
validateKind: validateYupSchema(kindSchema),
|
||||||
validateBulkDeleteInput: validateYupSchema(bulkDeleteInputSchema),
|
validateBulkActionInput: validateYupSchema(bulkActionInputSchema),
|
||||||
validateGenerateUIDInput: validateYupSchema(generateUIDInputSchema),
|
validateGenerateUIDInput: validateYupSchema(generateUIDInputSchema),
|
||||||
validateCheckUIDAvailabilityInput: validateYupSchema(checkUIDAvailabilityInputSchema),
|
validateCheckUIDAvailabilityInput: validateYupSchema(checkUIDAvailabilityInputSchema),
|
||||||
validateUIDField,
|
validateUIDField,
|
||||||
|
@ -323,6 +323,38 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
path: '/collection-types/:model/actions/bulkPublish',
|
||||||
|
handler: 'collection-types.bulkPublish',
|
||||||
|
config: {
|
||||||
|
middlewares: [routing],
|
||||||
|
policies: [
|
||||||
|
'plugin::content-manager.has-draft-and-publish',
|
||||||
|
'admin::isAuthenticatedAdmin',
|
||||||
|
{
|
||||||
|
name: 'plugin::content-manager.hasPermissions',
|
||||||
|
config: { actions: ['plugin::content-manager.explorer.delete'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
path: '/collection-types/:model/actions/bulkUnpublish',
|
||||||
|
handler: 'collection-types.bulkUnpublish',
|
||||||
|
config: {
|
||||||
|
middlewares: [routing],
|
||||||
|
policies: [
|
||||||
|
'plugin::content-manager.has-draft-and-publish',
|
||||||
|
'admin::isAuthenticatedAdmin',
|
||||||
|
{
|
||||||
|
name: 'plugin::content-manager.hasPermissions',
|
||||||
|
config: { actions: ['plugin::content-manager.explorer.delete'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/collection-types/:model/:id/actions/numberOfDraftRelations',
|
path: '/collection-types/:model/:id/actions/numberOfDraftRelations',
|
||||||
|
@ -5,6 +5,7 @@ const entityManagerLoader = require('../entity-manager');
|
|||||||
|
|
||||||
let entityManager;
|
let entityManager;
|
||||||
|
|
||||||
|
const queryUpdateMock = jest.fn(() => Promise.resolve());
|
||||||
describe('Content-Manager', () => {
|
describe('Content-Manager', () => {
|
||||||
const fakeModel = {
|
const fakeModel = {
|
||||||
modelName: 'fake model',
|
modelName: 'fake model',
|
||||||
@ -18,8 +19,15 @@ describe('Content-Manager', () => {
|
|||||||
entityService: {
|
entityService: {
|
||||||
update: jest.fn().mockReturnValue({ id: 1, publishedAt: new Date() }),
|
update: jest.fn().mockReturnValue({ id: 1, publishedAt: new Date() }),
|
||||||
},
|
},
|
||||||
|
db: {
|
||||||
|
query: jest.fn(() => ({
|
||||||
|
findMany: jest.fn().mockResolvedValue([{ id: 1 }, { id: 2 }]),
|
||||||
|
updateMany: queryUpdateMock,
|
||||||
|
})),
|
||||||
|
},
|
||||||
entityValidator: {
|
entityValidator: {
|
||||||
validateEntityCreation() {},
|
validateEntityCreation() {},
|
||||||
|
validateEntityUpdate: jest.fn().mockReturnValue([{ id: 1 }, { id: 2 }]),
|
||||||
},
|
},
|
||||||
eventHub: { emit: jest.fn(), sanitizeEntity: (entity) => entity },
|
eventHub: { emit: jest.fn(), sanitizeEntity: (entity) => entity },
|
||||||
getModel: jest.fn(() => fakeModel),
|
getModel: jest.fn(() => fakeModel),
|
||||||
@ -44,12 +52,32 @@ describe('Content-Manager', () => {
|
|||||||
populate: {},
|
populate: {},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Publish many content-types', async () => {
|
||||||
|
const uid = 'api::test.test';
|
||||||
|
const params = { filters: { $and: [1, 2] } };
|
||||||
|
|
||||||
|
await entityManager.publishMany(params, uid);
|
||||||
|
|
||||||
|
expect(strapi.db.query().updateMany).toBeCalledWith({
|
||||||
|
where: {
|
||||||
|
$and: [1, 2],
|
||||||
|
},
|
||||||
|
data: { publishedAt: expect.any(Date) },
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Unpublish', () => {
|
describe('Unpublish', () => {
|
||||||
const defaultConfig = {};
|
const defaultConfig = {};
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
global.strapi = {
|
global.strapi = {
|
||||||
|
db: {
|
||||||
|
query: jest.fn(() => ({
|
||||||
|
findMany: jest.fn().mockResolvedValue([{ id: 1 }, { id: 2 }]),
|
||||||
|
updateMany: queryUpdateMock,
|
||||||
|
})),
|
||||||
|
},
|
||||||
entityService: {
|
entityService: {
|
||||||
update: jest.fn().mockReturnValue({ id: 1, publishedAt: null }),
|
update: jest.fn().mockReturnValue({ id: 1, publishedAt: null }),
|
||||||
},
|
},
|
||||||
@ -76,5 +104,19 @@ describe('Content-Manager', () => {
|
|||||||
populate: {},
|
populate: {},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Unpublish many content-types', async () => {
|
||||||
|
const uid = 'api::test.test';
|
||||||
|
const params = { filters: { $and: [1, 2] } };
|
||||||
|
|
||||||
|
await entityManager.unpublishMany(params, uid);
|
||||||
|
|
||||||
|
expect(strapi.db.query().updateMany).toBeCalledWith({
|
||||||
|
where: {
|
||||||
|
$and: [1, 2],
|
||||||
|
},
|
||||||
|
data: { publishedAt: null },
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
const { assoc, has, prop, omit } = require('lodash/fp');
|
const { assoc, has, prop, omit } = require('lodash/fp');
|
||||||
const strapiUtils = require('@strapi/utils');
|
const strapiUtils = require('@strapi/utils');
|
||||||
const { mapAsync } = require('@strapi/utils');
|
const { mapAsync } = require('@strapi/utils');
|
||||||
|
const { transformParamsToQuery } = require('@strapi/utils').convertQueryParams;
|
||||||
const { ApplicationError } = require('@strapi/utils').errors;
|
const { ApplicationError } = require('@strapi/utils').errors;
|
||||||
const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate');
|
const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate');
|
||||||
const { getDeepRelationsCount } = require('./utils/count');
|
const { getDeepRelationsCount } = require('./utils/count');
|
||||||
@ -266,6 +267,77 @@ module.exports = ({ strapi }) => ({
|
|||||||
return mappedEntity;
|
return mappedEntity;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async publishMany(opts, uid) {
|
||||||
|
const params = {
|
||||||
|
...opts,
|
||||||
|
data: {
|
||||||
|
[PUBLISHED_AT_ATTRIBUTE]: new Date(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const query = transformParamsToQuery(uid, params);
|
||||||
|
const entitiesToUpdate = await strapi.db.query(uid).findMany(query);
|
||||||
|
// No entities to update, return early
|
||||||
|
if (!entitiesToUpdate.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate entities before publishing, throw if invalid
|
||||||
|
await Promise.all(
|
||||||
|
entitiesToUpdate.map((entityToUpdate) =>
|
||||||
|
strapi.entityValidator.validateEntityCreation(
|
||||||
|
strapi.getModel(uid),
|
||||||
|
entityToUpdate,
|
||||||
|
{
|
||||||
|
isDraft: true,
|
||||||
|
},
|
||||||
|
entityToUpdate
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// Everything is valid, publish
|
||||||
|
const publishedEntitiesCount = await strapi.db
|
||||||
|
.query(uid)
|
||||||
|
.updateMany({ ...query, data: params.data });
|
||||||
|
|
||||||
|
// Get the updated entities since updateMany only returns the count
|
||||||
|
const publishedEntities = await strapi.db.query(uid).findMany(query);
|
||||||
|
// Emit the publish event for all updated entities
|
||||||
|
await Promise.all(publishedEntities.map((entity) => emitEvent(ENTRY_PUBLISH, entity, uid)));
|
||||||
|
|
||||||
|
// Return the number of published entities
|
||||||
|
return publishedEntitiesCount;
|
||||||
|
},
|
||||||
|
|
||||||
|
async unpublishMany(opts, uid) {
|
||||||
|
const params = {
|
||||||
|
...opts,
|
||||||
|
data: {
|
||||||
|
[PUBLISHED_AT_ATTRIBUTE]: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const query = transformParamsToQuery(uid, params);
|
||||||
|
const entitiesToUpdate = await strapi.db.query(uid).findMany(query);
|
||||||
|
// No entities to update, return early
|
||||||
|
if (!entitiesToUpdate.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to validate, unpublish
|
||||||
|
const unpublishedEntitiesCount = await strapi.db
|
||||||
|
.query(uid)
|
||||||
|
.updateMany({ ...query, data: params.data });
|
||||||
|
|
||||||
|
// Get the updated entities since updateMany only returns the count
|
||||||
|
const unpublishedEntities = await strapi.db.query(uid).findMany(query);
|
||||||
|
// Emit the unpublish event for all updated entities
|
||||||
|
await Promise.all(unpublishedEntities.map((entity) => emitEvent(ENTRY_UNPUBLISH, entity, uid)));
|
||||||
|
|
||||||
|
// Return the number of unpublished entities
|
||||||
|
return unpublishedEntitiesCount;
|
||||||
|
},
|
||||||
|
|
||||||
async unpublish(entity, body = {}, uid) {
|
async unpublish(entity, body = {}, uid) {
|
||||||
if (!entity[PUBLISHED_AT_ATTRIBUTE]) {
|
if (!entity[PUBLISHED_AT_ATTRIBUTE]) {
|
||||||
throw new ApplicationError('already.draft');
|
throw new ApplicationError('already.draft');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user