feat!(content-manager): remove the ability to disable draft&publish (#18488)

* feat!: remove draft and publish triggering

* chore: update snapshots and tests

* test: adapt tests
This commit is contained in:
Alexandre BODIN 2023-10-24 10:57:31 +02:00 committed by Josh
parent df37b096ba
commit 5ae0dc40cd
140 changed files with 1033 additions and 1532 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,6 @@ describe('Admin Permissions - Conditions', () => {
singularName: 'article',
pluralName: 'articles',
displayName: 'Article',
draftAndPublish: true,
attributes: {
title: {
type: 'string',
@ -40,7 +39,6 @@ describe('Admin Permissions - Conditions', () => {
singularName: 'category',
pluralName: 'categories',
displayName: 'Category',
draftAndPublish: true,
attributes: {
name: {
type: 'string',

View File

@ -19,7 +19,6 @@ const baseWorkflow = {
const productUID = 'api::product.product';
const productModel = {
draftAndPublish: true,
pluginOptions: {},
singularName: 'product',
pluralName: 'products',
@ -34,7 +33,6 @@ const productModel = {
const articleUID = 'api::article.article';
const articleModel = {
draftAndPublish: true,
pluginOptions: {},
singularName: 'article',
pluralName: 'articles',

View File

@ -18,7 +18,6 @@ const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
const productUID = 'api::product.product';
const model = {
draftAndPublish: true,
pluginOptions: {},
singularName: 'product',
pluralName: 'products',

View File

@ -19,7 +19,6 @@ const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
const productUID = 'api::product.product';
const model = {
draftAndPublish: false,
pluginOptions: {},
singularName: 'product',
pluralName: 'products',
@ -55,6 +54,12 @@ describeOnCondition(edition === 'EE')('Review workflows', () => {
url: `/content-manager/collection-types/${uid}`,
body: data,
});
await requests.admin({
method: 'POST',
url: `/content-manager/collection-types/${uid}/${body.id}/actions/publish`,
});
return body;
};

View File

@ -80,7 +80,7 @@ describe('CM API - Basic + compo', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(product);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.productsWithCompo.push(res.body);
});
@ -92,7 +92,7 @@ describe('CM API - Basic + compo', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(data.productsWithCompo[0]);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
});
test('Update product with compo', async () => {
@ -115,7 +115,7 @@ describe('CM API - Basic + compo', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(product);
expect(res.body.id).toEqual(data.productsWithCompo[0].id);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.productsWithCompo[0] = res.body;
});
@ -128,7 +128,7 @@ describe('CM API - Basic + compo', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(data.productsWithCompo[0]);
expect(res.body.id).toEqual(data.productsWithCompo[0].id);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.productsWithCompo.shift();
});
@ -175,12 +175,18 @@ describe('CM API - Basic + compo', () => {
},
],
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-compo.product-with-compo',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product-with-compo.product-with-compo/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
@ -248,24 +254,30 @@ describe('CM API - Basic + compo', () => {
},
],
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-compo.product-with-compo',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product-with-compo.product-with-compo/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
status: 400,
name: 'ValidationError',
message: 'compo[0].name must be defined.',
message: 'compo[0].name must be a `string` type, but the final value was: `null`.',
details: {
errors: [
{
path: ['compo', '0', 'name'],
message: 'compo[0].name must be defined.',
message: 'compo[0].name must be a `string` type, but the final value was: `null`.',
name: 'ValidationError',
},
],

View File

@ -77,7 +77,7 @@ describe('CM API - Basic + compo', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(product);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.productsWithCompo.push(res.body);
});
@ -89,7 +89,7 @@ describe('CM API - Basic + compo', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(data.productsWithCompo[0]);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
});
test('Update product with compo', async () => {
@ -110,7 +110,7 @@ describe('CM API - Basic + compo', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(product);
expect(res.body.id).toEqual(data.productsWithCompo[0].id);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.productsWithCompo[0] = res.body;
});
@ -123,7 +123,7 @@ describe('CM API - Basic + compo', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(data.productsWithCompo[0]);
expect(res.body.id).toEqual(data.productsWithCompo[0].id);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.productsWithCompo.shift();
});
@ -152,28 +152,34 @@ describe('CM API - Basic + compo', () => {
});
describe('validation', () => {
test('Cannot create product with compo - compo required', async () => {
test('Cannot publish product with compo - compo required', async () => {
const product = {
name: 'Product 1',
description: 'Product description',
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-compo.product-with-compo',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product-with-compo.product-with-compo/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
message: 'compo must be defined.',
message: 'compo must be a `object` type, but the final value was: `null`.',
name: 'ValidationError',
details: {
errors: [
{
path: ['compo'],
message: 'compo must be defined.',
message: 'compo must be a `object` type, but the final value was: `null`.',
name: 'ValidationError',
},
],
@ -182,7 +188,7 @@ describe('CM API - Basic + compo', () => {
});
});
test('Cannot create product with compo - minLength', async () => {
test('Cannot publish product with compo - minLength', async () => {
const product = {
name: 'Product 1',
description: 'Product description',
@ -191,12 +197,18 @@ describe('CM API - Basic + compo', () => {
description: '',
},
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-compo.product-with-compo',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product-with-compo.product-with-compo/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
@ -225,6 +237,7 @@ describe('CM API - Basic + compo', () => {
description: 'A very long description that exceed the min length.',
},
};
const res = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-compo.product-with-compo',
@ -250,7 +263,7 @@ describe('CM API - Basic + compo', () => {
});
});
test('Cannot create product with compo - required', async () => {
test('Cannot publish product with compo - required', async () => {
const product = {
name: 'Product 1',
description: 'Product description',
@ -258,23 +271,29 @@ describe('CM API - Basic + compo', () => {
description: 'short',
},
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-compo.product-with-compo',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product-with-compo.product-with-compo/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
message: 'compo.name must be defined.',
message: 'compo.name must be a `string` type, but the final value was: `null`.',
name: 'ValidationError',
details: {
errors: [
{
path: ['compo', 'name'],
message: 'compo.name must be defined.',
message: 'compo.name must be a `string` type, but the final value was: `null`.',
name: 'ValidationError',
},
],

View File

@ -43,7 +43,6 @@ const productWithCompoAndDP = {
repeatable: true,
},
},
draftAndPublish: true,
displayName: 'product with compo and DP',
singularName: 'product-with-compo-and-dp',
pluralName: 'product-with-compo-and-dps',
@ -51,7 +50,7 @@ const productWithCompoAndDP = {
collectionName: '',
};
describe('CM API - Basic + compo + draftAndPublish', () => {
describe('CM API - Basic + compo', () => {
beforeAll(async () => {
await builder.addComponent(compo).addContentType(productWithCompoAndDP).build();

View File

@ -42,7 +42,6 @@ const productWithCompoAndDP = {
required: true,
},
},
draftAndPublish: true,
displayName: 'product with compo and DP',
singularName: 'product-with-compo-and-dp',
pluralName: 'product-with-compo-and-dps',
@ -50,7 +49,7 @@ const productWithCompoAndDP = {
collectionName: '',
};
describe('CM API - Basic + compo + draftAndPublish', () => {
describe('CM API - Basic + compo', () => {
beforeAll(async () => {
await builder.addComponent(compo).addContentType(productWithCompoAndDP).build();

View File

@ -42,7 +42,6 @@ const productWithCompoAndDP = {
required: true,
},
},
draftAndPublish: true,
displayName: 'product with dz and DP',
singularName: 'product-with-dz-and-dp',
pluralName: 'product-with-dz-and-dps',
@ -50,7 +49,7 @@ const productWithCompoAndDP = {
collectionName: '',
};
describe('CM API - Basic + dz + draftAndPublish', () => {
describe('CM API - Basic + dz', () => {
beforeAll(async () => {
await builder.addComponent(compo).addContentType(productWithCompoAndDP).build();

View File

@ -25,7 +25,6 @@ const productWithDP = {
maxLength: 30,
},
},
draftAndPublish: true,
displayName: 'product with DP',
singularName: 'product-with-dp',
pluralName: 'product-with-dps',
@ -48,7 +47,7 @@ const compo = {
},
};
describe('CM API - Basic + draftAndPublish', () => {
describe('CM API - Basic', () => {
beforeAll(async () => {
await builder.addComponent(compo).addContentType(productWithDP).build();

View File

@ -80,7 +80,7 @@ describe('CM API - Basic + dz', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(product);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBe(null);
data.productsWithDz.push(res.body);
});
@ -92,7 +92,7 @@ describe('CM API - Basic + dz', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(data.productsWithDz[0]);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBe(null);
});
test('Update product with compo', async () => {
@ -116,7 +116,7 @@ describe('CM API - Basic + dz', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(product);
expect(res.body.id).toEqual(data.productsWithDz[0].id);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBe(null);
data.productsWithDz[0] = res.body;
});
@ -129,7 +129,7 @@ describe('CM API - Basic + dz', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(data.productsWithDz[0]);
expect(res.body.id).toEqual(data.productsWithDz[0].id);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBe(null);
data.productsWithDz.shift();
});
@ -162,7 +162,7 @@ describe('CM API - Basic + dz', () => {
});
describe('validation', () => {
test('Cannot create product with compo - compo required', async () => {
test('Cannot publish product with compo - compo required', async () => {
const product = {
name: 'Product 1',
description: 'Product description',
@ -205,12 +205,17 @@ describe('CM API - Basic + dz', () => {
},
],
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-dz.product-with-dz',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product-with-dz.product-with-dz/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
@ -280,24 +285,30 @@ describe('CM API - Basic + dz', () => {
},
],
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-dz.product-with-dz',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product-with-dz.product-with-dz/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
status: 400,
name: 'ValidationError',
message: 'dz[0].name must be defined.',
message: 'dz[0].name must be a `string` type, but the final value was: `null`.',
details: {
errors: [
{
path: ['dz', '0', 'name'],
message: 'dz[0].name must be defined.',
message: 'dz[0].name must be a `string` type, but the final value was: `null`.',
name: 'ValidationError',
},
],
@ -317,6 +328,7 @@ describe('CM API - Basic + dz', () => {
},
],
};
const res = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product-with-dz.product-with-dz',

View File

@ -70,7 +70,7 @@ describe('CM API - Basic', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(omit('hiddenAttribute', product));
expect(res.body).not.toHaveProperty('hiddenAttribute');
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.products.push(res.body);
});
@ -91,7 +91,7 @@ describe('CM API - Basic', () => {
}),
])
);
res.body.results.forEach((p) => expect(p.publishedAt).toBeUndefined());
res.body.results.forEach((p) => expect(p.publishedAt).toBeDefined());
});
test('Update product', async () => {
@ -109,7 +109,7 @@ describe('CM API - Basic', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(omit('hiddenAttribute', product));
expect(res.body.id).toEqual(data.products[0].id);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.products[0] = res.body;
});
@ -122,7 +122,7 @@ describe('CM API - Basic', () => {
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(data.products[0]);
expect(res.body.id).toEqual(data.products[0].id);
expect(res.body.publishedAt).toBeUndefined();
expect(res.body.publishedAt).toBeDefined();
data.products.shift();
});
@ -176,17 +176,23 @@ describe('CM API - Basic', () => {
});
describe('validators', () => {
test('Cannot create a product - minLength', async () => {
test('Cannot publish a product - minLength', async () => {
const product = {
name: 'Product 1',
description: '',
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product.product',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product.product/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
@ -206,27 +212,33 @@ describe('CM API - Basic', () => {
});
});
test('Cannot create a product - required', async () => {
test('Cannot publish a product - required', async () => {
const product = {
description: 'Product description',
};
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product.product',
body: product,
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::product.product/${creationRes.body.id}/actions/publish`,
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
message: 'name must be defined.',
message: 'name must be a `string` type, but the final value was: `null`.',
name: 'ValidationError',
details: {
errors: [
{
path: ['name'],
message: 'name must be defined.',
message: 'name must be a `string` type, but the final value was: `null`.',
name: 'ValidationError',
},
],
@ -240,6 +252,7 @@ describe('CM API - Basic', () => {
name: 'Product 1',
description: "I'm a product description that is very long. At least thirty characters.",
};
const res = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::product.product',

View File

@ -64,7 +64,6 @@ const categoryDPModel = {
displayName: 'Category Draft & Publish',
singularName: 'categorydp',
pluralName: 'categoriesdp',
draftAndPublish: true,
attributes: {
name: {
type: 'string',
@ -123,6 +122,12 @@ describe('CM API - Basic', () => {
url: '/content-manager/collection-types/api::category.category',
body: { name: 'Food' },
});
await rq({
method: 'POST',
url: `/content-manager/collection-types/api::category.category/${category.id}/actions/publish`,
});
data.categories.push(category);
const { body: categoryPublished } = await rq({
@ -130,10 +135,12 @@ describe('CM API - Basic', () => {
url: '/content-manager/collection-types/api::categorydp.categorydp',
body: { name: 'Food' },
});
await rq({
method: 'POST',
url: `/content-manager/collection-types/api::categorydp.categorydp/${categoryPublished.id}/actions/publish`,
});
data.categoriesdp.published.push(categoryPublished);
const { body: categoryDraft1 } = await rq({
@ -141,6 +148,7 @@ describe('CM API - Basic', () => {
url: '/content-manager/collection-types/api::categorydp.categorydp',
body: { name: 'Food' },
});
data.categoriesdp.draft.push(categoryDraft1);
const { body: categoryDraft2 } = await rq({
@ -148,6 +156,7 @@ describe('CM API - Basic', () => {
url: '/content-manager/collection-types/api::categorydp.categorydp',
body: { name: 'Food' },
});
data.categoriesdp.draft.push(categoryDraft2);
});

View File

@ -105,8 +105,8 @@ describe('Non repeatable and required component', () => {
}
);
test('Throws when sending a null value', async () => {
const res = await rq.post('/', {
test('Throws when publishing a null value', async () => {
const creationRes = await rq.post('/', {
body: {
field: null,
},
@ -115,17 +115,21 @@ describe('Non repeatable and required component', () => {
},
});
const res = await rq.post(`/${creationRes.body.id}/actions/publish`);
expect(res.statusCode).toBe(400);
});
test('Throws when the component is not provided', async () => {
const res = await rq.post('/', {
const creationRes = await rq.post('/', {
body: {},
qs: {
populate: ['field'],
},
});
const res = await rq.post(`/${creationRes.body.id}/actions/publish`);
expect(res.statusCode).toBe(400);
});
});
@ -231,8 +235,8 @@ describe('Non repeatable and required component', () => {
});
});
test('Throws if component is null', async () => {
const res = await rq.post('/', {
test('Throws when publishing if component is null', async () => {
const creationRes = await rq.post('/', {
body: {
field: {
name: 'someString',
@ -243,7 +247,7 @@ describe('Non repeatable and required component', () => {
},
});
const updateRes = await rq.put(`/${res.body.id}`, {
const updateRes = await rq.put(`/${creationRes.body.id}`, {
body: {
field: null,
},
@ -252,16 +256,18 @@ describe('Non repeatable and required component', () => {
},
});
expect(updateRes.statusCode).toBe(400);
const res = await rq.post(`/${updateRes.body.id}/actions/publish`);
const getRes = await rq.get(`/${res.body.id}`, {
expect(res.statusCode).toBe(400);
const getRes = await rq.get(`/${creationRes.body.id}`, {
qs: {
populate: ['field'],
},
});
expect(getRes.statusCode).toBe(200);
expect(getRes.body).toMatchObject(res.body);
expect(getRes.body).toMatchObject(updateRes.body);
});
test('Replaces the previous component if sent without id', async () => {

View File

@ -155,7 +155,7 @@ describe('Test type UID', () => {
});
test('Cannot set value to be null', async () => {
const res = await rq.post(
const createRes = await rq.post(
'/content-manager/collection-types/api::withrequireduid.withrequireduid',
{
body: {
@ -164,6 +164,10 @@ describe('Test type UID', () => {
}
);
const res = await rq.post(
`/content-manager/collection-types/api::withrequireduid.withrequireduid/${createRes.body.id}/actions/publish`
);
expect(res.statusCode).toBe(400);
});
});

View File

@ -36,8 +36,7 @@ const compo = (withRelations = false) => ({
},
});
const productModel = (draftAndPublish = false) => ({
draftAndPublish,
const productModel = () => ({
attributes: {
name: {
type: 'string',
@ -50,8 +49,7 @@ const productModel = (draftAndPublish = false) => ({
collectionName: '',
});
const shopModel = (draftAndPublish = false) => ({
draftAndPublish,
const shopModel = () => ({
attributes: {
name: {
type: 'string',
@ -110,15 +108,14 @@ const createEntry = async (uid, data) => {
return body;
};
describe.each([[false], [true]])('Relations, with d&p: %p', (withDraftAndPublish) => {
describe('Relations, with d&p: %p', () => {
const builder = createTestBuilder();
const addPublishedAtCheck = (value) => (withDraftAndPublish ? { publishedAt: value } : undefined);
const addPublishedAtCheck = (value) => {
publishedAt: value;
};
beforeAll(async () => {
await builder
.addComponent(compo(false))
.addContentTypes([productModel(withDraftAndPublish), shopModel(withDraftAndPublish)])
.build();
await builder.addComponent(compo(false)).addContentTypes([productModel(), shopModel()]).build();
await modelsUtils.modifyComponent(compo(true));
@ -128,12 +125,10 @@ describe.each([[false], [true]])('Relations, with d&p: %p', (withDraftAndPublish
const createdProduct1 = await createEntry('api::product.product', { name: 'Skate' });
const createdProduct2 = await createEntry('api::product.product', { name: 'Candle' });
if (withDraftAndPublish) {
await rq({
url: `/content-manager/collection-types/api::product.product/${createdProduct1.id}/actions/publish`,
method: 'POST',
});
}
await rq({
url: `/content-manager/collection-types/api::product.product/${createdProduct1.id}/actions/publish`,
method: 'POST',
});
data.products.push(createdProduct1);
data.products.push(createdProduct2);

View File

@ -36,8 +36,7 @@ const compo = (withRelations = false) => ({
},
});
const productModel = (draftAndPublish = false) => ({
draftAndPublish,
const productModel = () => ({
attributes: {
name: {
type: 'string',
@ -50,8 +49,7 @@ const productModel = (draftAndPublish = false) => ({
collectionName: '',
});
const shopModel = (draftAndPublish = false) => ({
draftAndPublish,
const shopModel = () => ({
attributes: {
name: {
type: 'string',
@ -110,15 +108,14 @@ const createEntry = async (uid, data) => {
return body;
};
describe.each([false, true])('Relations, with d&p: %s', (withDraftAndPublish) => {
describe('Relations', () => {
const builder = createTestBuilder();
const addPublishedAtCheck = (value) => (withDraftAndPublish ? { publishedAt: value } : undefined);
const addPublishedAtCheck = (value) => {
publishedAt: value;
};
beforeAll(async () => {
await builder
.addComponent(compo(false))
.addContentTypes([productModel(withDraftAndPublish), shopModel(withDraftAndPublish)])
.build();
await builder.addComponent(compo(false)).addContentTypes([productModel(), shopModel()]).build();
await modelsUtils.modifyComponent(compo(true));
@ -129,12 +126,10 @@ describe.each([false, true])('Relations, with d&p: %s', (withDraftAndPublish) =>
const createdProduct2 = await createEntry('api::product.product', { name: 'Candle' });
const createdProduct3 = await createEntry('api::product.product', { name: 'Tofu' });
if (withDraftAndPublish) {
await rq({
url: `/content-manager/collection-types/api::product.product/${createdProduct1.id}/actions/publish`,
method: 'POST',
});
}
await rq({
url: `/content-manager/collection-types/api::product.product/${createdProduct1.id}/actions/publish`,
method: 'POST',
});
data.products.push(createdProduct1);
data.products.push(createdProduct2);

View File

@ -99,7 +99,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
});
test('Create tag2', async () => {
@ -123,7 +123,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
});
test('Create tag3', async () => {
@ -147,7 +147,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
});
test('Create article1 without relation', async () => {
@ -176,7 +176,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const tags = (await getRelations('article', 'tags', body.id)).results;
expect(tags.length).toBe(0);
@ -208,7 +208,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const tags = (await getRelations('article', 'tags', body.id)).results;
expect(tags.length).toBe(1);
@ -239,7 +239,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const tags = (await getRelations('article', 'tags', body.id)).results;
expect(tags.length).toBe(1);
@ -266,7 +266,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const tags = (await getRelations('article', 'tags', body.id)).results;
expect(tags.length).toBe(3);
@ -292,7 +292,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const tags = (await getRelations('article', 'tags', body.id)).results;
expect(tags.length).toBe(2);
@ -322,7 +322,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const tags = (await getRelations('article', 'tags', body.id)).results;
expect(tags.length).toBe(0);
@ -484,7 +484,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const articles = (await getRelations('category', 'articles', body.id)).results;
expect(articles.length).toBe(0);
@ -511,7 +511,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const articles = (await getRelations('category', 'articles', body.id)).results;
expect(articles.length).toBe(0);
});
@ -542,7 +542,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
const tags = (await getRelations('article', 'tags', body.id)).results;
expect(tags.length).toBe(0);
@ -816,7 +816,7 @@ describe('Relations', () => {
id: 1,
username: null,
});
expect(body.publishedAt).toBeUndefined();
expect(body.publishedAt).toBeDefined();
});
test('Update article1 with ref1', async () => {

View File

@ -18,7 +18,6 @@ exports[`Content Type Builder - Content types Collection Types Get collection ty
"collectionName": "test_collection_types",
"description": "",
"displayName": "Test Collection Type",
"draftAndPublish": false,
"kind": "collectionType",
"pluginOptions": {
"i18n": {
@ -35,7 +34,7 @@ exports[`Content Type Builder - Content types Collection Types Get collection ty
}
`;
exports[`Content Type Builder - Content types Collection Types Get collection type returns full schema and informations with draftAndPublish 1`] = `
exports[`Content Type Builder - Content types Collection Types Get collection type returns full schema and informations 1`] = `
{
"data": {
"apiID": "ct-with-dp",
@ -48,7 +47,6 @@ exports[`Content Type Builder - Content types Collection Types Get collection ty
"collectionName": "ct_with_dps",
"description": "",
"displayName": "CT with DP",
"draftAndPublish": true,
"kind": "collectionType",
"pluralName": "ct-with-dps",
"restrictRelationsTo": null,

View File

@ -18,7 +18,6 @@ exports[`Content Type Builder - Content types Single Types Get single type retur
"collectionName": "test_single_types",
"description": "",
"displayName": "Test Single Type",
"draftAndPublish": false,
"kind": "singleType",
"pluginOptions": {
"i18n": {

View File

@ -114,7 +114,7 @@ describe('Content Type Builder - Content types', () => {
expect(res.body).toMatchSnapshot();
});
test('Successful creation of a collection type with draftAndPublish enabled', async () => {
test('Successful creation of a collection type', async () => {
const res = await rq({
method: 'POST',
url: '/content-type-builder/content-types',
@ -123,7 +123,6 @@ describe('Content Type Builder - Content types', () => {
displayName: 'CT with DP',
singularName: 'ct-with-dp',
pluralName: 'ct-with-dps',
draftAndPublish: true,
attributes: {
title: {
type: 'string',
@ -141,7 +140,7 @@ describe('Content Type Builder - Content types', () => {
});
});
test('Get collection type returns full schema and informations with draftAndPublish', async () => {
test('Get collection type returns full schema and informations', async () => {
const res = await rq({
method: 'GET',
url: `/content-type-builder/content-types/${ctWithDpUID}`,
@ -208,7 +207,6 @@ describe('Content Type Builder - Content types', () => {
displayName: 'same string',
singularName: 'same-string',
pluralName: 'same-string',
draftAndPublish: true,
attributes: {
title: {
type: 'string',
@ -242,7 +240,6 @@ describe('Content Type Builder - Content types', () => {
url: '/content-type-builder/content-types',
body: {
contentType: {
draftAndPublish: true,
attributes: {
title: {
type: 'string',
@ -295,7 +292,6 @@ describe('Content Type Builder - Content types', () => {
displayName: 'new displayName',
singularName: 'ct-with-dp-new',
pluralName: 'ct-with-dps-new',
draftAndPublish: true,
attributes: {
title: {
type: 'string',
@ -321,7 +317,6 @@ describe('Content Type Builder - Content types', () => {
displayName: 'new displayName',
singularName: 'ct-with-dp', // no change
pluralName: 'ct-with-dps',
draftAndPublish: true,
attributes: {
title: {
type: 'string',
@ -350,7 +345,6 @@ describe('Content Type Builder - Content types', () => {
displayName: 'same string',
singularName,
pluralName,
draftAndPublish: true,
attributes: {
title: {
type: 'string',

View File

@ -91,7 +91,7 @@ describe('Core API - Basic + compo', () => {
attributes: product,
});
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.productsWithCompo.push(body.data);
});
@ -110,7 +110,7 @@ describe('Core API - Basic + compo', () => {
expect(body.data[0]).toMatchObject(data.productsWithCompo[0]);
body.data.forEach((p) => {
expect(p.attributes.publishedAt).toBeUndefined();
expect(p.attributes.publishedAt).toBeDefined();
});
});
@ -144,7 +144,7 @@ describe('Core API - Basic + compo', () => {
attributes: product,
});
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
expect(body.data.publishedAt).toBeUndefined();
data.productsWithCompo[0] = body.data;
@ -162,7 +162,7 @@ describe('Core API - Basic + compo', () => {
expect(statusCode).toBe(200);
expect(body.data).toMatchObject(data.productsWithCompo[0]);
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.productsWithCompo.shift();
});

View File

@ -92,7 +92,7 @@ describe('Core API - Basic + compo', () => {
attributes: product,
});
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.productsWithCompo.push(body.data);
});
@ -110,7 +110,7 @@ describe('Core API - Basic + compo', () => {
expect(body.data).toHaveLength(1);
expect(body.data[0]).toMatchObject(data.productsWithCompo[0]);
body.data.forEach((p) => {
expect(p.attributes.publishedAt).toBeUndefined();
expect(p.attributes.publishedAt).toBeDefined();
});
});
@ -141,7 +141,7 @@ describe('Core API - Basic + compo', () => {
attributes: product,
});
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.productsWithCompo[0] = body.data;
});
@ -157,7 +157,7 @@ describe('Core API - Basic + compo', () => {
expect(statusCode).toBe(200);
expect(body.data).toMatchObject(data.productsWithCompo[0]);
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.productsWithCompo.shift();
});

View File

@ -41,7 +41,6 @@ const productWithCompoAndDP = {
repeatable: true,
},
},
draftAndPublish: true,
displayName: 'product-with-compo-and-dp',
singularName: 'product-with-compo-and-dp',
pluralName: 'product-with-compo-and-dps',
@ -49,7 +48,7 @@ const productWithCompoAndDP = {
collectionName: '',
};
describe('Core API - Basic + compo + draftAndPublish', () => {
describe('Core API - Basic + compo ', () => {
beforeAll(async () => {
await builder.addComponent(compo).addContentType(productWithCompoAndDP).build();

View File

@ -40,7 +40,6 @@ const productWithCompoAndDP = {
required: true,
},
},
draftAndPublish: true,
displayName: 'product-with-compo-and-dp',
singularName: 'product-with-compo-and-dp',
pluralName: 'product-with-compo-and-dps',
@ -48,7 +47,7 @@ const productWithCompoAndDP = {
collectionName: '',
};
describe('Core API - Basic + compo + draftAndPublish', () => {
describe('Core API - Basic + compo', () => {
beforeAll(async () => {
await builder.addComponent(compo).addContentType(productWithCompoAndDP).build();

View File

@ -40,7 +40,6 @@ const productWithDP = {
maxLength: 30,
},
},
draftAndPublish: true,
displayName: 'product-with-dp',
singularName: 'product-with-dp',
pluralName: 'product-with-dps',
@ -48,7 +47,7 @@ const productWithDP = {
collectionName: '',
};
describe('Core API - Basic + draftAndPublish', () => {
describe('Core API - Basic', () => {
beforeAll(async () => {
await builder.addComponent(compo).addContentType(productWithDP).build();

View File

@ -91,7 +91,7 @@ describe('Core API - Basic + dz', () => {
attributes: product,
});
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.productWithDz.push(body.data);
});
@ -109,7 +109,7 @@ describe('Core API - Basic + dz', () => {
expect(body.data).toHaveLength(1);
expect(body.data[0]).toMatchObject(data.productWithDz[0]);
body.data.forEach((p) => {
expect(p.attributes.publishedAt).toBeUndefined();
expect(p.attributes.publishedAt).toBeDefined();
});
});
@ -143,7 +143,7 @@ describe('Core API - Basic + dz', () => {
attributes: product,
});
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.productWithDz[0] = body.data;
});
@ -159,7 +159,7 @@ describe('Core API - Basic + dz', () => {
expect(statusCode).toBe(200);
expect(body.data).toMatchObject(data.productWithDz[0]);
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.productWithDz.shift();
});

View File

@ -77,7 +77,7 @@ describe('Core API - Basic', () => {
attributes: product,
},
});
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.product.push(body.data);
});
@ -106,7 +106,7 @@ describe('Core API - Basic', () => {
);
body.data.forEach((p) => {
expect(p.attributes.publishedAt).toBeUndefined();
expect(p.attributes.publishedAt).toBeDefined();
});
});
@ -131,7 +131,7 @@ describe('Core API - Basic', () => {
});
expect(body.data.id).toEqual(data.product[0].id);
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.product[0] = res.body.data;
});
@ -147,7 +147,7 @@ describe('Core API - Basic', () => {
expect(statusCode).toBe(200);
expect(body.data).toMatchObject(data.product[0]);
expect(body.data.id).toEqual(data.product[0].id);
expect(body.data.attributes.publishedAt).toBeUndefined();
expect(body.data.attributes.publishedAt).toBeDefined();
data.product.shift();
});
});

View File

@ -742,7 +742,14 @@ describe('Core API - Validate', () => {
*/
describe('Fields', () => {
const allDocumentFields = ['name', 'name_non_searchable', 'misc', 'createdAt', 'updatedAt'];
const allDocumentFields = [
'name',
'name_non_searchable',
'misc',
'createdAt',
'updatedAt',
'publishedAt',
];
describe('No fields param', () => {
it('Select all fields sort when no fields param is provided', async () => {

View File

@ -1,183 +0,0 @@
'use strict';
const _ = require('lodash');
const { createTestBuilder } = require('api-tests/builder');
const { createStrapiInstance } = require('api-tests/strapi');
const { createAuthRequest } = require('api-tests/request');
const modelsUtils = require('api-tests/models');
let builder;
let strapi;
let rq;
const data = {
dogs: [],
};
const dogModel = {
draftAndPublish: false,
attributes: {
name: {
type: 'string',
},
code: {
type: 'string',
unique: true,
},
},
singularName: 'dog',
pluralName: 'dogs',
displayName: 'Dog',
description: '',
collectionName: '',
};
const dogs = [
{
name: 'Nelson',
code: '1',
},
{
name: 'Atos',
code: '2',
},
];
const restart = async () => {
await strapi.destroy();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
};
const sortDogs = (dogs) => _.sortBy(dogs, 'name');
describe('Migration - draft and publish', () => {
describe.each([
['without table modifications', {}, {}],
['with table modifications', { town: { type: 'string' } }, { color: { type: 'string' } }],
])('%p', (testName, tableModification1, tableModification2) => {
beforeAll(async () => {
builder = createTestBuilder();
await builder.addContentType(dogModel).addFixtures(dogModel.singularName, dogs).build();
strapi = await createStrapiInstance();
rq = await createAuthRequest({ strapi });
data.dogs = sortDogs(await builder.sanitizedFixturesFor(dogModel.singularName, strapi));
});
afterAll(async () => {
await strapi.destroy();
await builder.cleanup();
});
describe('Enabling D&P on a content-type', () => {
test('No publishedAt before enabling the feature', async () => {
const { body } = await rq({
url: '/content-manager/collection-types/api::dog.dog',
method: 'GET',
});
expect(body.results.length).toBe(2);
const sortedBody = sortDogs(body.results);
sortedBody.forEach((dog, index) => {
expect(dog).toMatchObject(data.dogs[index]);
expect(dog.publishedAt).toBeUndefined();
});
});
test('Published_at is equal to created_at after enabling the feature', async () => {
const schema = await modelsUtils.getContentTypeSchema(dogModel.singularName, { strapi });
await modelsUtils.modifyContentType(
{
...schema,
attributes: _.merge(schema.attributes, tableModification1),
draftAndPublish: true,
},
{ strapi }
);
await restart();
const { body } = await rq({
method: 'GET',
url: '/content-manager/collection-types/api::dog.dog',
});
expect(body.results.length).toBe(2);
const sortedBody = sortDogs(body.results);
sortedBody.forEach((dog, index) => {
expect(dog).toMatchObject(data.dogs[index]);
expect(dog.publishedAt).toBe(dog.createdAt || dog.created_at);
expect(!Number.isNaN(new Date(dog.publishedAt).valueOf())).toBe(true);
});
data.dogs = sortedBody;
});
});
describe('Disabling D&P on a content-type', () => {
test('No publishedAt after disabling the feature + draft removed', async () => {
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::dog.dog/${data.dogs[1].id}/actions/unpublish`,
});
data.dogs[1] = res.body;
const schema = await modelsUtils.getContentTypeSchema(dogModel.singularName, { strapi });
await modelsUtils.modifyContentType(
{
...schema,
draftAndPublish: false,
attributes: _.merge(schema.attributes, tableModification2),
},
{ strapi }
);
await restart();
// drafts should have been deleted with the migration, so we remove them
data.dogs = data.dogs.filter((dog) => !_.isNil(dog.publishedAt));
const { body } = await rq({
url: '/content-manager/collection-types/api::dog.dog',
method: 'GET',
});
expect(body.results.length).toBe(1);
expect(body.results[0]).toMatchObject(_.pick(data.dogs[0], ['name']));
expect(body.results[0].publishedAt).toBeUndefined();
});
test('Unique constraint is kept after disabling the feature', async () => {
const dogToCreate = { code: 'sameCode' };
let res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::dog.dog/`,
body: dogToCreate,
});
expect(res.statusCode).toBe(200);
expect(res.body).toMatchObject(dogToCreate);
data.dogs.push(res.body);
res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::dog.dog/`,
body: dogToCreate,
});
expect(res.statusCode).toBe(400);
});
});
});
});

View File

@ -13,7 +13,6 @@ const data = {
};
const dogModel = {
draftAndPublish: false,
attributes: {
name: {
type: 'string',
@ -69,7 +68,7 @@ describe('Migration - required attribute', () => {
expect(dogWithNameNull).toBeTruthy();
});
test('Cannot create an entry with null after migration', async () => {
test('Cannot publish an entry with null after migration', async () => {
// remove null values otherwise the migration would fail
const { body } = await rq({
@ -87,12 +86,17 @@ describe('Migration - required attribute', () => {
await restart();
// Try to create an entry with null
const res = await rq({
const creationRes = await rq({
method: 'POST',
url: '/content-manager/collection-types/api::dog.dog',
body: { name: null },
});
const res = await rq({
method: 'POST',
url: `/content-manager/collection-types/api::dog.dog/${creationRes.body.id}/actions/publish`,
});
expect(res.body).toMatchObject({
data: null,
error: {

View File

@ -13,7 +13,6 @@ const data = {
};
const dogModel = {
draftAndPublish: false,
attributes: {
name: {
type: 'string',

View File

@ -60,7 +60,6 @@ const contentTypes = {
required: true,
},
},
draftAndPublish: true,
displayName: 'Product',
singularName: 'product',
pluralName: 'products',
@ -73,7 +72,6 @@ const contentTypes = {
type: 'string',
},
},
draftAndPublish: true,
displayName: 'Country',
singularName: 'country',
pluralName: 'countries',
@ -86,7 +84,6 @@ const contentTypes = {
type: 'string',
},
},
draftAndPublish: true,
displayName: 'Category',
singularName: 'category',
pluralName: 'categories',

View File

@ -16,7 +16,6 @@ const model = {
displayName: 'single-type',
singularName: 'single-type',
pluralName: 'single-types',
draftAndPublish: true,
attributes: {
title: {
type: 'string',

View File

@ -20,7 +20,6 @@ const documentModel = {
type: 'richtext',
},
},
draftAndPublish: true,
singularName: 'document',
pluralName: 'documents',
displayName: 'Document',

View File

@ -15,9 +15,7 @@ const categoryModel = {
pluralName: 'categories',
description: '',
name: 'Category',
options: {
draftAndPublish: false,
},
options: {},
pluginOptions: {
i18n: {
localized: true,
@ -41,9 +39,7 @@ const homepageModel = {
displayName: 'Homepage',
singularName: 'homepage',
pluralName: 'homepages',
options: {
draftAndPublish: false,
},
options: {},
pluginOptions: {
i18n: {
localized: true,

View File

@ -31,9 +31,7 @@ const categoryModel = {
pluralName: 'categories',
description: '',
name: 'Category',
options: {
draftAndPublish: false,
},
options: {},
pluginOptions: {
i18n: {
localized: true,
@ -52,9 +50,7 @@ const dogSchema = {
displayName: 'Dog',
singularName: 'dog',
pluralName: 'dogs',
options: {
draftAndPublish: false,
},
options: {},
pluginOptions: {
i18n: {
localized: true,

View File

@ -15,9 +15,7 @@ const categoryModel = {
pluralName: 'categories',
description: '',
name: 'Category',
options: {
draftAndPublish: false,
},
options: {},
pluginOptions: {
i18n: {
localized: true,

View File

@ -114,7 +114,9 @@ describe('i18n - Find available relations', () => {
});
expect(res.body.results).toHaveLength(1);
expect(res.body.results[0]).toStrictEqual(pick(['id', 'name'], data.products[1]));
expect(res.body.results[0]).toStrictEqual(
pick(['id', 'name', 'publishedAt'], data.products[1])
);
});
test('Can filter on any locale', async () => {
@ -125,6 +127,8 @@ describe('i18n - Find available relations', () => {
});
expect(res.body.results).toHaveLength(1);
expect(res.body.results[0]).toStrictEqual(pick(['id', 'name'], data.products[0]));
expect(res.body.results[0]).toStrictEqual(
pick(['id', 'name', 'publishedAt'], data.products[0])
);
});
});

View File

@ -116,7 +116,9 @@ describe('i18n - Find existing relations', () => {
});
expect(res.body.results).toHaveLength(1);
expect(res.body.results[0]).toStrictEqual(pick(['id', 'name'], data.products[0]));
expect(res.body.results[0]).toStrictEqual(
pick(['id', 'name', 'publishedAt'], data.products[0])
);
});
test('Get english product for english shop', async () => {
@ -126,6 +128,8 @@ describe('i18n - Find existing relations', () => {
});
expect(res.body.results).toHaveLength(1);
expect(res.body.results[0]).toStrictEqual(pick(['id', 'name'], data.products[1]));
expect(res.body.results[0]).toStrictEqual(
pick(['id', 'name', 'publishedAt'], data.products[1])
);
});
});

View File

@ -262,7 +262,6 @@ describe('Advanced Test GraphQL Users API End to End', () => {
singularName: 'user',
pluralName: 'users',
description: '',
draftAndPublish: false,
kind: 'collectionType',
collectionName: 'up_users',
attributes: {

View File

@ -95,8 +95,8 @@ telemetry.send('event_name', { key: 'value' });
- `userProperties`: <Type>Object</Type> An object that defines the identity of the user who triggered the event.
- `groupProperties`: <Type>Object</Type> An object that defines properties of the application or environment in which the event occurred.
Examples of event properties in Strapi include model, containsRelationalFields, displayedFields, kind, and hasDraftAndPublish. These properties are specific to the event and are used to provide additional context about what happened.
Examples of event properties in Strapi include model, containsRelationalFields, displayedFields, kind. These properties are specific to the event and are used to provide additional context about what happened.
User properties can include information such as the user's operating system, node version, and hostname. These properties are typically used to group events by user or to filter events based on certain user characteristics.
Group properties can include information such as the language(s) used in the application, the database being used, and the number of locales. These properties are typically used to group events by application version, environment, or other characteristics.
Group properties can include information such as the language(s) used in the application, the database being used, and the number of locales. These properties are typically used to group events by application version, environment, or other characteristics.

View File

@ -6,9 +6,7 @@
"pluralName": "testings",
"displayName": "testing"
},
"options": {
"draftAndPublish": true
},
"options": {},
"attributes": {
"title": {
"type": "string"

View File

@ -8,10 +8,7 @@
"description": "",
"name": "Address"
},
"options": {
"reviewWorkflows": true,
"draftAndPublish": false
},
"options": {},
"pluginOptions": {},
"attributes": {
"postal_code": {

View File

@ -9,8 +9,7 @@
"name": "Category"
},
"options": {
"reviewWorkflows": true,
"draftAndPublish": true
"reviewWorkflows": true
},
"pluginOptions": {
"i18n": {

View File

@ -9,7 +9,6 @@
"name": "Country"
},
"options": {
"draftAndPublish": false,
"comment": ""
},
"pluginOptions": {

View File

@ -6,9 +6,7 @@
"singularName": "homepage",
"pluralName": "homepages"
},
"options": {
"draftAndPublish": true
},
"options": {},
"pluginOptions": {
"i18n": {
"localized": true

View File

@ -8,9 +8,7 @@
"description": "",
"name": "Kitchen Sink"
},
"options": {
"draftAndPublish": true
},
"options": {},
"pluginOptions": {},
"attributes": {
"short_text": {
@ -60,33 +58,19 @@
},
"enumeration": {
"type": "enumeration",
"enum": [
"A",
"B",
"C",
"D",
"E"
]
"enum": ["A", "B", "C", "D", "E"]
},
"single_media": {
"type": "media",
"multiple": false,
"required": false,
"allowedTypes": [
"images",
"files",
"videos"
]
"allowedTypes": ["images", "files", "videos"]
},
"multiple_media": {
"type": "media",
"multiple": true,
"required": false,
"allowedTypes": [
"images",
"files",
"videos"
]
"allowedTypes": ["images", "files", "videos"]
},
"json": {
"type": "json"
@ -103,10 +87,7 @@
},
"dynamiczone": {
"type": "dynamiczone",
"components": [
"basic.simple",
"blog.test-como"
]
"components": ["basic.simple", "blog.test-como"]
},
"one_way_tag": {
"type": "relation",
@ -162,10 +143,7 @@
},
"cats": {
"type": "dynamiczone",
"components": [
"basic.relation",
"basic.simple"
]
"components": ["basic.relation", "basic.simple"]
}
}
}

View File

@ -8,7 +8,6 @@
"description": ""
},
"options": {
"draftAndPublish": false,
"comment": ""
},
"attributes": {

View File

@ -8,7 +8,6 @@
"pluralName": "menus"
},
"options": {
"draftAndPublish": false,
"comment": ""
},
"attributes": {

View File

@ -8,7 +8,6 @@
"description": ""
},
"options": {
"draftAndPublish": false,
"comment": ""
},
"attributes": {

View File

@ -7,9 +7,7 @@
"displayName": "Relations",
"description": ""
},
"options": {
"draftAndPublish": true
},
"options": {},
"pluginOptions": {
"i18n": {
"localized": true
@ -47,10 +45,7 @@
}
},
"type": "dynamiczone",
"components": [
"basic.relation",
"basic.simple"
]
"components": ["basic.relation", "basic.simple"]
},
"single_relation": {
"type": "component",

View File

@ -9,7 +9,6 @@
"name": "Restaurant"
},
"options": {
"draftAndPublish": true,
"populateCreatorFields": true,
"comment": ""
},
@ -36,13 +35,7 @@
"pluginOptions": {}
},
"priceRange": {
"enum": [
"very_cheap",
"cheap",
"average",
"expensive",
"very_expensive"
],
"enum": ["very_cheap", "cheap", "average", "expensive", "very_expensive"],
"type": "enumeration",
"pluginOptions": {
"i18n": {

View File

@ -8,7 +8,6 @@
"description": ""
},
"options": {
"draftAndPublish": false,
"comment": ""
},
"attributes": {

View File

@ -7,9 +7,7 @@
"pluralName": "tags",
"description": ""
},
"options": {
"draftAndPublish": true
},
"options": {},
"pluginOptions": {},
"attributes": {
"name": {

View File

@ -8,9 +8,7 @@
"name": "temp",
"description": ""
},
"options": {
"draftAndPublish": true
},
"options": {},
"pluginOptions": {},
"attributes": {
"name": {

View File

@ -6,9 +6,7 @@
"pluralName": "tests",
"description": ""
},
"options": {
"draftAndPublish": false
},
"options": {},
"pluginOptions": {
"i18n": {
"localized": true

View File

@ -7,9 +7,7 @@
"pluralName": "users",
"displayName": "User"
},
"options": {
"draftAndPublish": false
},
"options": {},
"attributes": {
"username": {
"type": "string",

View File

@ -5,9 +5,7 @@
"pluralName": "tests",
"description": ""
},
"options": {
"draftAndPublish": false
},
"options": {},
"pluginOptions": {
"i18n": {
"localized": true

View File

@ -6,9 +6,7 @@
"pluralName": "testings",
"displayName": "testing"
},
"options": {
"draftAndPublish": true
},
"options": {},
"attributes": {
"title": {
"type": "string"

View File

@ -342,7 +342,6 @@ const address = {
kind: 'collectionType',
info: { displayName: 'addresse', name: 'address', description: '', label: 'Addresses' },
options: {
draftAndPublish: true,
increments: true,
timestamps: ['createdAt', 'updatedAt'],
comment: '',

View File

@ -77,13 +77,9 @@ const EditViewDataManagerProvider = ({
const currentContentTypeLayout = get(allLayoutData, ['contentType'], {});
const hasDraftAndPublish = useMemo(() => {
return get(currentContentTypeLayout, ['options', 'draftAndPublish'], false);
}, [currentContentTypeLayout]);
const shouldNotRunValidations = useMemo(() => {
return hasDraftAndPublish && !initialData.publishedAt;
}, [hasDraftAndPublish, initialData.publishedAt]);
return !initialData.publishedAt;
}, [initialData.publishedAt]);
const { trackUsage } = useTracking();
const { formatMessage } = useIntl();
@ -358,12 +354,8 @@ const EditViewDataManagerProvider = ({
);
const trackerProperty = useMemo(() => {
if (!hasDraftAndPublish) {
return {};
}
return shouldNotRunValidations ? { status: 'draft' } : {};
}, [hasDraftAndPublish, shouldNotRunValidations]);
}, [shouldNotRunValidations]);
const handlePublishPromptDismissal = useCallback(async (e) => {
e.preventDefault();
@ -619,7 +611,6 @@ const EditViewDataManagerProvider = ({
checkFormErrors,
createActionAllowedFields,
formErrors,
hasDraftAndPublish,
initialData,
isCreatingEntry,
isSingleType,

View File

@ -2,18 +2,15 @@ import { useCMEditViewDataManager } from '@strapi/helper-plugin';
import isEmpty from 'lodash/isEmpty';
function useSelect() {
const { hasDraftAndPublish, modifiedData } = useCMEditViewDataManager();
const { modifiedData } = useCMEditViewDataManager();
let trackerProperty = {};
if (hasDraftAndPublish) {
const isDraft = isEmpty(modifiedData.publishedAt);
const isDraft = isEmpty(modifiedData.publishedAt);
trackerProperty = isDraft ? { status: 'draft' } : { status: 'published' };
}
trackerProperty = isDraft ? { status: 'draft' } : { status: 'published' };
return {
hasDraftAndPublish,
trackerProperty,
};
}

View File

@ -19,13 +19,9 @@ const CustomBullet = styled(Dot)`
}
`;
const DraftAndPublishBadge = ({ hasDraftAndPublish, isPublished }) => {
const DraftAndPublishBadge = ({ isPublished }) => {
const { formatMessage } = useIntl();
if (!hasDraftAndPublish) {
return null;
}
const colors = {
draft: {
textColor: 'secondary700',
@ -85,7 +81,6 @@ const DraftAndPublishBadge = ({ hasDraftAndPublish, isPublished }) => {
};
DraftAndPublishBadge.propTypes = {
hasDraftAndPublish: PropTypes.bool.isRequired,
isPublished: PropTypes.bool.isRequired,
};

View File

@ -24,7 +24,7 @@ const makeApp = (props) => (
describe('<DraftAndPublishBadge />', () => {
it('renders and matches the snapshot', () => {
const App = makeApp({ hasDraftAndPublish: true, isPublished: true });
const App = makeApp({ isPublished: true });
const {
container: { firstChild },
@ -124,7 +124,7 @@ describe('<DraftAndPublishBadge />', () => {
});
it('should show the draft design when it is not published', () => {
const App = makeApp({ hasDraftAndPublish: true, isPublished: false });
const App = makeApp({ isPublished: false });
const {
container: { firstChild },
@ -222,12 +222,4 @@ describe('<DraftAndPublishBadge />', () => {
</aside>
`);
});
it('should show return null when hasDraftAndPublish is falsy', () => {
const App = makeApp({ hasDraftAndPublish: false, isPublished: false });
const { queryByText } = render(App);
expect(queryByText('Editing')).not.toBeInTheDocument();
});
});

View File

@ -1,12 +1,11 @@
import { useCMEditViewDataManager } from '@strapi/helper-plugin';
function useSelect() {
const { initialData, hasDraftAndPublish } = useCMEditViewDataManager();
const { initialData } = useCMEditViewDataManager();
const isPublished = initialData.publishedAt !== undefined && initialData.publishedAt !== null;
return {
hasDraftAndPublish,
isPublished,
};
}

View File

@ -35,7 +35,6 @@ const Header = ({
initialData,
isCreatingEntry,
isSingleType,
hasDraftAndPublish,
layout,
modifiedData,
onPublish,
@ -73,11 +72,9 @@ const Header = ({
if (isCreatingEntry && canCreate) {
primaryAction = (
<Flex gap={2}>
{hasDraftAndPublish && (
<Button disabled startIcon={<Check />} variant="secondary">
{formatMessage({ id: 'app.utils.publish', defaultMessage: 'Publish' })}
</Button>
)}
<Button disabled startIcon={<Check />} variant="secondary">
{formatMessage({ id: 'app.utils.publish', defaultMessage: 'Publish' })}
</Button>
<Button disabled={!didChangeData} loading={status === 'submit-pending'} type="submit">
{formatMessage({
id: getTrad('containers.Edit.submit'),
@ -89,7 +86,7 @@ const Header = ({
}
if (!isCreatingEntry && canUpdate) {
const shouldShowPublishButton = hasDraftAndPublish && canPublish;
const shouldShowPublishButton = canPublish;
const isPublished = !isEmpty(initialData.publishedAt);
const isPublishButtonLoading = isPublished
? status === 'unpublish-pending'
@ -286,7 +283,6 @@ Header.propTypes = {
isSingleType: PropTypes.bool.isRequired,
status: PropTypes.string.isRequired,
layout: PropTypes.object.isRequired,
hasDraftAndPublish: PropTypes.bool.isRequired,
modifiedData: PropTypes.object.isRequired,
onPublish: PropTypes.func.isRequired,
onUnpublish: PropTypes.func.isRequired,

View File

@ -6,7 +6,6 @@
"isDisplayed": true,
"info": { "name": "restaurant", "description": "" },
"options": {
"draftAndPublish": false,
"increments": true,
"timestamps": ["createdAt", "updatedAt"],
"comment": ""

View File

@ -22,7 +22,6 @@ const defaultProps = {
initialData: {},
isCreatingEntry: true,
isSingleType: false,
hasDraftAndPublish: false,
layout: ct,
modifiedData: {},
onPublish: jest.fn(),
@ -76,7 +75,7 @@ describe('CONTENT MANAGER | EditView | Header', () => {
color: #ffffff;
}
.c14 {
.c15 {
font-size: 1rem;
line-height: 1.5;
color: #666687;
@ -207,6 +206,8 @@ describe('CONTENT MANAGER | EditView | Header', () => {
.c12 {
height: 2rem;
border: 1px solid #d9d8ff;
background: #f0f0ff;
}
.c12 svg {
@ -243,17 +244,81 @@ describe('CONTENT MANAGER | EditView | Header', () => {
}
.c12:hover {
border: 1px solid #7b79ff;
background: #7b79ff;
background-color: #ffffff;
}
.c12:active {
background-color: #ffffff;
border: 1px solid #4945ff;
background: #4945ff;
}
.c12:active .c3 {
color: #4945ff;
}
.c12:active svg > g,
.c12:active svg path {
fill: #4945ff;
}
.c12 .c3 {
color: #271fe0;
}
.c12 svg > g,
.c12 svg path {
fill: #271fe0;
}
.c14 {
height: 2rem;
}
.c14 svg {
height: 0.75rem;
width: auto;
}
.c14[aria-disabled='true'] {
border: 1px solid #dcdce4;
background: #eaeaef;
}
.c14[aria-disabled='true'] .c3 {
color: #666687;
}
.c14[aria-disabled='true'] svg > g,
.c14[aria-disabled='true'] svg path {
fill: #666687;
}
.c14[aria-disabled='true']:active {
border: 1px solid #dcdce4;
background: #eaeaef;
}
.c14[aria-disabled='true']:active .c3 {
color: #666687;
}
.c14[aria-disabled='true']:active svg > g,
.c14[aria-disabled='true']:active svg path {
fill: #666687;
}
.c14:hover {
border: 1px solid #7b79ff;
background: #7b79ff;
}
.c14:active {
border: 1px solid #4945ff;
background: #4945ff;
}
.c14 svg > g,
.c14 svg path {
fill: #ffffff;
}
@ -372,6 +437,35 @@ describe('CONTENT MANAGER | EditView | Header', () => {
aria-disabled="true"
class="c10 c9 c11 c12"
disabled=""
type="button"
>
<div
aria-hidden="true"
class=""
>
<svg
fill="none"
height="1rem"
viewBox="0 0 24 24"
width="1rem"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20.727 2.97a.2.2 0 0 1 .286 0l2.85 2.89a.2.2 0 0 1 0 .28L9.554 20.854a.2.2 0 0 1-.285 0l-9.13-9.243a.2.2 0 0 1 0-.281l2.85-2.892a.2.2 0 0 1 .284 0l6.14 6.209L20.726 2.97Z"
fill="#212134"
/>
</svg>
</div>
<span
class="c3 c13"
>
Publish
</span>
</button>
<button
aria-disabled="true"
class="c10 c9 c11 c14"
disabled=""
type="submit"
>
<span
@ -383,7 +477,7 @@ describe('CONTENT MANAGER | EditView | Header', () => {
</div>
</div>
<p
class="c3 c14"
class="c3 c15"
>
API ID : restaurant
</p>

View File

@ -7,7 +7,6 @@ function useSelect() {
isSingleType,
status,
layout,
hasDraftAndPublish,
modifiedData,
onPublish,
onUnpublish,
@ -21,7 +20,6 @@ function useSelect() {
isSingleType,
status,
layout,
hasDraftAndPublish,
modifiedData,
onPublish,
onUnpublish,

View File

@ -34,7 +34,7 @@ export const TableRows = ({
canCreate,
canDelete,
contentType,
features: { hasDraftAndPublish, hasReviewWorkflows },
features: { hasReviewWorkflows },
headers,
entriesToDelete,
onClickDelete,
@ -153,7 +153,7 @@ export const TableRows = ({
)}
{headers.map(({ key, cellFormatter, name, ...rest }) => {
if (hasDraftAndPublish && name === 'publishedAt') {
if (name === 'publishedAt') {
return (
<Td key={key}>
<Status
@ -292,7 +292,6 @@ TableRows.propTypes = {
}).isRequired,
entriesToDelete: PropTypes.array,
features: PropTypes.shape({
hasDraftAndPublish: PropTypes.bool.isRequired,
hasReviewWorkflows: PropTypes.bool.isRequired,
}).isRequired,
headers: PropTypes.array.isRequired,

View File

@ -193,7 +193,6 @@ function ListView({ canCreate, canDelete, canRead, canPublish, layout, slug }) {
return filter;
});
const hasDraftAndPublish = options?.draftAndPublish ?? false;
const hasReviewWorkflows = options?.reviewWorkflows ?? false;
const reviewWorkflowColumns = useEnterprise(
@ -507,23 +506,21 @@ function ListView({ canCreate, canDelete, canRead, canPublish, layout, slug }) {
};
});
if (hasDraftAndPublish) {
formattedHeaders.push({
key: '__published_at_temp_key__',
name: 'publishedAt',
fieldSchema: {
type: 'custom',
},
metadatas: {
label: formatMessage({
id: getTrad(`containers.ListPage.table-headers.publishedAt`),
defaultMessage: 'publishedAt',
}),
searchable: false,
sortable: true,
},
});
}
formattedHeaders.push({
key: '__published_at_temp_key__',
name: 'publishedAt',
fieldSchema: {
type: 'custom',
},
metadatas: {
label: formatMessage({
id: getTrad(`containers.ListPage.table-headers.publishedAt`),
defaultMessage: 'publishedAt',
}),
searchable: false,
sortable: true,
},
});
if (reviewWorkflowColumns) {
// Make sure the column header label is translated
@ -539,14 +536,7 @@ function ListView({ canCreate, canDelete, canRead, canPublish, layout, slug }) {
}
return formattedHeaders;
}, [
runHookWaterfall,
displayedHeaders,
layout,
reviewWorkflowColumns,
hasDraftAndPublish,
formatMessage,
]);
}, [runHookWaterfall, displayedHeaders, layout, reviewWorkflowColumns, formatMessage]);
const subtitle = canRead
? formatMessage(
@ -564,7 +554,7 @@ function ListView({ canCreate, canDelete, canRead, canPublish, layout, slug }) {
{...props}
forwardedAs={ReactRouterLink}
onClick={() => {
const trackerProperty = hasDraftAndPublish ? { status: 'draft' } : {};
const trackerProperty = { status: 'draft' };
trackUsageRef.current('willCreateEntry', trackerProperty);
}}
@ -691,7 +681,7 @@ function ListView({ canCreate, canDelete, canRead, canPublish, layout, slug }) {
<Table.Root rows={data} isLoading={isLoading} colCount={colCount}>
<Table.ActionBar>
<BulkActionButtons
showPublish={canPublish && hasDraftAndPublish}
showPublish={canPublish}
showDelete={canDelete}
onConfirmDeleteAll={handleConfirmDeleteAllData}
onConfirmUnpublishAll={handleConfirmUnpublishAllData}
@ -738,7 +728,7 @@ function ListView({ canCreate, canDelete, canRead, canPublish, layout, slug }) {
</Td>
{/* Field data */}
{tableHeaders.map(({ key, name, cellFormatter, ...rest }) => {
if (hasDraftAndPublish && name === 'publishedAt') {
if (name === 'publishedAt') {
return (
<Td key={key}>
<Status

View File

@ -62,44 +62,26 @@ const StyledTable = styled(Table)`
}
`;
const getCEHeaders = (isDraftAndPublish) => {
const getCEHeaders = () => {
const headers = [
{ id: 'Settings.webhooks.events.create', defaultMessage: 'Create' },
{ id: 'Settings.webhooks.events.update', defaultMessage: 'Update' },
{ id: 'app.utils.delete', defaultMessage: 'Delete' },
{ id: 'app.utils.publish', defaultMessage: 'Publish' },
{ id: 'app.utils.unpublish', defaultMessage: 'Unpublish' },
];
if (isDraftAndPublish) {
headers.push({ id: 'app.utils.publish', defaultMessage: 'Publish' });
headers.push({ id: 'app.utils.unpublish', defaultMessage: 'Unpublish' });
}
return headers;
};
const getCEEvents = (isDraftAndPublish) => {
const entryEvents = ['entry.create', 'entry.update', 'entry.delete'];
if (isDraftAndPublish) {
entryEvents.push('entry.publish', 'entry.unpublish');
}
return {
entry: entryEvents,
media: ['media.create', 'media.update', 'media.delete'],
};
const CEEvents = {
entry: ['entry.create', 'entry.update', 'entry.delete', 'entry.publish', 'entry.unpublish'],
media: ['media.create', 'media.update', 'media.delete'],
};
const WebhookEventContext = React.createContext();
const Root = ({ children }) => {
const { formatMessage } = useIntl();
const { collectionTypes, isLoading } = useContentTypes();
const isDraftAndPublish = React.useMemo(
() => collectionTypes.some((ct) => ct.options.draftAndPublish === true),
[collectionTypes]
);
const { isLoading } = useContentTypes();
const label = formatMessage({
id: 'Settings.webhooks.form.events',
@ -107,20 +89,18 @@ const Root = ({ children }) => {
});
return (
<WebhookEventContext.Provider value={{ isDraftAndPublish }}>
<Flex direction="column" alignItems="stretch" gap={1}>
<FieldLabel aria-hidden>{label}</FieldLabel>
{isLoading && (
<Loader>
{formatMessage({
id: 'Settings.webhooks.events.isLoading',
defaultMessage: 'Events loading',
})}
</Loader>
)}
<StyledTable aria-label={label}>{children}</StyledTable>
</Flex>
</WebhookEventContext.Provider>
<Flex direction="column" alignItems="stretch" gap={1}>
<FieldLabel aria-hidden>{label}</FieldLabel>
{isLoading && (
<Loader>
{formatMessage({
id: 'Settings.webhooks.events.isLoading',
defaultMessage: 'Events loading',
})}
</Loader>
)}
<StyledTable aria-label={label}>{children}</StyledTable>
</Flex>
);
};
@ -129,10 +109,8 @@ Root.propTypes = {
};
const Headers = ({ getHeaders = getCEHeaders }) => {
const { isDraftAndPublish } = React.useContext(WebhookEventContext);
const { formatMessage } = useIntl();
const headers = getHeaders(isDraftAndPublish);
const headers = getHeaders();
return (
<Thead>
@ -184,9 +162,7 @@ Headers.propTypes = {
};
const Body = ({ providedEvents }) => {
const { isDraftAndPublish } = React.useContext(WebhookEventContext);
const events = providedEvents || getCEEvents(isDraftAndPublish);
const events = providedEvents || CEEvents;
const { values, handleChange: onChange } = useFormikContext();
const inputName = 'events';

View File

@ -736,8 +736,6 @@ components:
maxItems: 2
items:
type: string
draftAndPublish:
type: boolean
attributes:
type: object
additionalProperties:

View File

@ -106,7 +106,7 @@ describe('Relations', () => {
'target',
expect.objectContaining({
sort: 'myField',
fields: ['id', 'myField'],
fields: ['id', 'myField', 'publishedAt'],
filters: {
$and: [
{
@ -146,7 +146,7 @@ describe('Relations', () => {
'targetWithHidden',
expect.objectContaining({
sort: 'id',
fields: ['id'],
fields: ['id', 'publishedAt'],
filters: {
$and: [
{
@ -190,7 +190,7 @@ describe('Relations', () => {
{ id: 1 },
'relation',
expect.objectContaining({
fields: ['id', 'myField'],
fields: ['id', 'myField', 'publishedAt'],
})
);
});
@ -220,7 +220,7 @@ describe('Relations', () => {
{ id: 1 },
'relationWithHidden',
expect.objectContaining({
fields: ['id'],
fields: ['id', 'publishedAt'],
})
);
});
@ -266,7 +266,7 @@ describe('Relations', () => {
{ id: 1 },
'relationWithHidden',
expect.objectContaining({
fields: ['id'],
fields: ['id', 'publishedAt'],
})
);
});

View File

@ -131,9 +131,7 @@ describe('Single Types', () => {
},
getModel() {
return {
options: {
draftAndPublish: true,
},
options: {},
attributes: {
title: {
type: 'string',
@ -248,9 +246,7 @@ describe('Single Types', () => {
},
getModel() {
return {
options: {
draftAndPublish: true,
},
options: {},
attributes: {
title: {
type: 'string',
@ -346,9 +342,7 @@ describe('Single Types', () => {
},
getModel() {
return {
options: {
draftAndPublish: true,
},
options: {},
attributes: {
title: {
type: 'string',
@ -444,9 +438,7 @@ describe('Single Types', () => {
},
getModel() {
return {
options: {
draftAndPublish: true,
},
options: {},
attributes: {
title: {
type: 'string',

View File

@ -1,7 +1,6 @@
'use strict';
const { prop, isEmpty, uniq, flow } = require('lodash/fp');
const { hasDraftAndPublish } = require('@strapi/utils').contentTypes;
const { isAnyToMany } = require('@strapi/utils').relations;
const { PUBLISHED_AT_ATTRIBUTE } = require('@strapi/utils').contentTypes.constants;
const { isOperatorOfType } = require('@strapi/utils');
@ -122,10 +121,7 @@ module.exports = {
(mainField) => sanitizeMainField(targetedModel, mainField, userAbility)
)(modelConfig);
const fieldsToSelect = uniq(['id', mainField]);
if (hasDraftAndPublish(targetedModel)) {
fieldsToSelect.push(PUBLISHED_AT_ATTRIBUTE);
}
const fieldsToSelect = uniq(['id', mainField, PUBLISHED_AT_ATTRIBUTE]);
const queryParams = {
sort: mainField,
@ -233,10 +229,7 @@ module.exports = {
(mainField) => sanitizeMainField(targetedModel, mainField, userAbility)
)(modelConfig);
const fieldsToSelect = uniq(['id', mainField]);
if (hasDraftAndPublish(targetedModel)) {
fieldsToSelect.push(PUBLISHED_AT_ATTRIBUTE);
}
const fieldsToSelect = uniq(['id', mainField, PUBLISHED_AT_ATTRIBUTE]);
const queryParams = {
fields: fieldsToSelect,

View File

@ -1,57 +0,0 @@
'use strict';
const hasDraftAndPublish = require('../has-draft-and-publish');
describe('hasDraftAndPublish policy', () => {
beforeEach(() => {
global.strapi = {
errors: {
forbidden: jest.fn(() => 'forbidden'),
},
contentTypes: {
foo: {
options: {
draftAndPublish: true,
},
},
bar: {
options: {
draftAndPublish: false,
},
},
},
};
});
afterEach(() => {
jest.clearAllMocks();
});
test('It should succeed when the model has draft & publish enabled', () => {
const ctx = { params: { model: 'foo' } };
const res = hasDraftAndPublish(ctx, {}, { strapi: global.strapi });
expect(res).toBe(true);
});
test(`It should fail when the model has draft & publish disabled`, () => {
const ctx = { params: { model: 'bar' } };
const res = hasDraftAndPublish(ctx, {}, { strapi: global.strapi });
expect(res).toBe(false);
});
test(`It should fail when the model doesn't exists`, () => {
const ctx = { params: { model: 'foobar' } };
const res = hasDraftAndPublish(ctx, {}, { strapi: global.strapi });
expect(res).toBe(false);
});
test(`It should fail when params.model isn't provided`, () => {
const ctx = { params: {} };
const res = hasDraftAndPublish(ctx, {}, { strapi: global.strapi });
expect(res).toBe(false);
});
});

View File

@ -1,13 +0,0 @@
'use strict';
const {
contentTypes: { hasDraftAndPublish },
} = require('@strapi/utils');
module.exports = (ctx, config, { strapi }) => {
const { model: modelUID } = ctx.params;
const model = strapi.contentTypes[modelUID];
return hasDraftAndPublish(model);
};

View File

@ -1,9 +1,7 @@
'use strict';
const hasDraftAndPublish = require('./has-draft-and-publish');
const hasPermissions = require('./hasPermissions');
module.exports = {
'has-draft-and-publish': hasDraftAndPublish,
hasPermissions,
};

View File

@ -161,7 +161,6 @@ module.exports = {
config: {
middlewares: [routing],
policies: [
'plugin::content-manager.has-draft-and-publish',
'admin::isAuthenticatedAdmin',
{
name: 'plugin::content-manager.hasPermissions',
@ -177,7 +176,6 @@ module.exports = {
config: {
middlewares: [routing],
policies: [
'plugin::content-manager.has-draft-and-publish',
'admin::isAuthenticatedAdmin',
{
name: 'plugin::content-manager.hasPermissions',
@ -313,7 +311,6 @@ module.exports = {
config: {
middlewares: [routing],
policies: [
'plugin::content-manager.has-draft-and-publish',
'admin::isAuthenticatedAdmin',
{
name: 'plugin::content-manager.hasPermissions',
@ -329,7 +326,6 @@ module.exports = {
config: {
middlewares: [routing],
policies: [
'plugin::content-manager.has-draft-and-publish',
'admin::isAuthenticatedAdmin',
{
name: 'plugin::content-manager.hasPermissions',
@ -360,7 +356,6 @@ module.exports = {
config: {
middlewares: [routing],
policies: [
'plugin::content-manager.has-draft-and-publish',
'admin::isAuthenticatedAdmin',
{
name: 'plugin::content-manager.hasPermissions',
@ -376,7 +371,6 @@ module.exports = {
config: {
middlewares: [routing],
policies: [
'plugin::content-manager.has-draft-and-publish',
'admin::isAuthenticatedAdmin',
{
name: 'plugin::content-manager.hasPermissions',

View File

@ -13,7 +13,6 @@ const {
ALLOWED_WEBHOOK_EVENTS: { ENTRY_PUBLISH, ENTRY_UNPUBLISH },
} = require('../constants');
const { hasDraftAndPublish } = strapiUtils.contentTypes;
const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
@ -89,13 +88,10 @@ module.exports = ({ strapi }) => ({
},
async create(body, uid) {
const modelDef = strapi.getModel(uid);
const publishData = { ...body };
const populate = await buildDeepPopulate(uid);
if (hasDraftAndPublish(modelDef)) {
publishData[PUBLISHED_AT_ATTRIBUTE] = null;
}
publishData[PUBLISHED_AT_ATTRIBUTE] = null;
const params = { data: publishData, populate };
@ -126,13 +122,10 @@ module.exports = ({ strapi }) => ({
return updatedEntity;
},
async clone(entity, body, uid) {
const modelDef = strapi.getModel(uid);
const populate = await buildDeepPopulate(uid);
const publishData = { ...body };
if (hasDraftAndPublish(modelDef)) {
publishData[PUBLISHED_AT_ATTRIBUTE] = null;
}
publishData[PUBLISHED_AT_ATTRIBUTE] = null;
const params = {
data: publishData,

View File

@ -17,10 +17,6 @@ module.exports = ({ strapi }) => ({
const displayedContentTypes = getService('content-types').findDisplayedContentTypes();
const contentTypesUids = displayedContentTypes.map(prop('uid'));
const draftAndPublishContentTypesUids = displayedContentTypes
.filter(contentTypesUtils.hasDraftAndPublish)
.map(prop('uid'));
const actions = [
{
section: 'contentTypes',
@ -64,7 +60,7 @@ module.exports = ({ strapi }) => ({
displayName: 'Publish',
uid: 'explorer.publish',
pluginName: 'content-manager',
subjects: draftAndPublishContentTypesUids,
subjects: contentTypesUids,
},
{
section: 'plugins',

View File

@ -3,7 +3,7 @@
const { castArray } = require('lodash/fp');
const strapiUtils = require('@strapi/utils');
const { hasDraftAndPublish, isVisibleAttribute } = strapiUtils.contentTypes;
const { isVisibleAttribute } = strapiUtils.contentTypes;
/**
* sumDraftCounts works recursively on the attributes of a model counting the
* number of draft relations
@ -24,8 +24,7 @@ const sumDraftCounts = (entity, uid) => {
switch (attribute.type) {
case 'relation': {
const childModel = strapi.getModel(attribute.target);
if (hasDraftAndPublish(childModel) && isVisibleAttribute(model, attributeName)) {
if (isVisibleAttribute(model, attributeName)) {
return sum + value.count;
}
return sum;

View File

@ -3,7 +3,7 @@
const { merge, isEmpty, set, propEq } = require('lodash/fp');
const strapiUtils = require('@strapi/utils');
const { hasDraftAndPublish, isVisibleAttribute } = strapiUtils.contentTypes;
const { isVisibleAttribute } = strapiUtils.contentTypes;
const { isAnyToMany } = strapiUtils.relations;
const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
@ -168,8 +168,7 @@ const getDeepPopulateDraftCount = (uid) => {
switch (attribute.type) {
case 'relation': {
const childModel = strapi.getModel(attribute.target);
if (hasDraftAndPublish(childModel) && isVisibleAttribute(model, attributeName)) {
if (isVisibleAttribute(model, attributeName)) {
populateAcc[attributeName] = {
count: true,
filters: { [PUBLISHED_AT_ATTRIBUTE]: { $null: true } },

View File

@ -144,7 +144,6 @@ const createContentTypeSchema = ({
},
})
.required(errorsTrads.required),
draftAndPublish: yup.boolean(),
kind: yup.string().oneOf(['singleType', 'collectionType']),
reviewWorkflows: yup.boolean(),
};

View File

@ -15,22 +15,7 @@ const forms = {
return {
sections: [
{
items: [
{
intlLabel: {
id: getTrad('contentType.draftAndPublish.label'),
defaultMessage: 'Draft & publish',
},
description: {
id: getTrad('contentType.draftAndPublish.description'),
defaultMessage:
'Allows writing a draft version of an entry, before it is published',
},
name: 'draftAndPublish',
type: 'toggle-draft-publish',
validations: {},
},
],
items: [],
},
],
};

View File

@ -184,35 +184,29 @@ const FormModal = () => {
});
}
// Create content type we need to add the default option draftAndPublish
// Create content type
if (modalType === 'contentType' && actionType === 'create') {
dispatch({
type: SET_DATA_TO_EDIT,
modalType,
actionType,
data: {
draftAndPublish: true,
},
data: {},
pluginOptions: {},
});
}
// Edit content type
if (modalType === 'contentType' && actionType === 'edit') {
const {
displayName,
draftAndPublish,
kind,
pluginOptions,
pluralName,
reviewWorkflows,
singularName,
} = get(allDataSchema, [...pathToSchema, 'schema'], {
displayName: null,
pluginOptions: {},
singularName: null,
pluralName: null,
});
const { displayName, kind, pluginOptions, pluralName, reviewWorkflows, singularName } = get(
allDataSchema,
[...pathToSchema, 'schema'],
{
displayName: null,
pluginOptions: {},
singularName: null,
pluralName: null,
}
);
dispatch({
type: SET_DATA_TO_EDIT,
@ -220,7 +214,6 @@ const FormModal = () => {
modalType,
data: {
displayName,
draftAndPublish,
kind,
pluginOptions,
pluralName,

View File

@ -5,7 +5,6 @@ export default {
schema: {
name: 'Address',
description: '',
draftAndPublish: false,
pluginOptions: {
i18n: {
localized: true,

View File

@ -50,8 +50,6 @@
"contentType.collectionName.description": "Nützlich wenn sich der Name der Sammlung und der Tabellenname unterscheiden",
"contentType.collectionName.label": "Name der Sammlung",
"contentType.displayName.label": "Anzeigename",
"contentType.draftAndPublish.description": "Lege einen Entwurf des Eintrags an bevor er veröffentlicht wird",
"contentType.draftAndPublish.label": "Entwurf/Veröffentlichen-System",
"contentType.kind.change.warning": "Du hast die Art eines Inhaltstyps geändert: API wird resettet (Routen, Controller und Services werden überschrieben).",
"error.attributeName.reserved-name": "Dieser Name kann nicht für Attribute genutzt werden, da er andere Funktionalitäten beeinträchtigen würde",
"error.contentType.pluralName-used": "Dieser Wert kann nicht gleich sein wie der Singular-Wert",

View File

@ -50,8 +50,6 @@
"contentType.collectionName.description": "Brugbar når navnet på din indholdstype og dit tabel navn er forskellige",
"contentType.collectionName.label": "Dokument navn",
"contentType.displayName.label": "Visningsnavn",
"contentType.draftAndPublish.description": "Opret en version som udkast for hvert element inden det offentliggøres",
"contentType.draftAndPublish.label": "Udkast/offentliggør system",
"contentType.kind.change.warning": "Du har lige ændret typen af en indholdstype: API bliver nustillet (ruter, controllere, og services bliver overskrevet).",
"error.attributeName.reserved-name": "Dette navn kan ikke bruges i din indholdstype, da det måske kan ødelægge andre funktioner",
"error.contentType.pluralName-used": "Denne værdi kan ikke være den samme som ved ental",

View File

@ -53,8 +53,6 @@
"contentType.collectionName.description": "Useful when the name of your Content Type and your table name differ",
"contentType.collectionName.label": "Collection name",
"contentType.displayName.label": "Display name",
"contentType.draftAndPublish.description": "Allows writing a draft version of an entry, before it is published",
"contentType.draftAndPublish.label": "Draft & publish",
"contentType.kind.change.warning": "You just changed the kind of a content type: API will be reset (routes, controllers, and services will be overwritten).",
"error.attributeName.reserved-name": "This name cannot be used in your content type as it might break other functionalities",
"error.contentType.pluralName-used": "This value cannot be the same as the singular one",

View File

@ -50,8 +50,6 @@
"contentType.collectionName.description": "Útil cuando el nombre de su Tipo de Contenido y el nombre de su tabla difieren",
"contentType.collectionName.label": "Nombre de la colección",
"contentType.displayName.label": "Nombre para mostrar",
"contentType.draftAndPublish.description": "Escribe un borrador de cada entrada antes de publicarla.",
"contentType.draftAndPublish.label": "Sistema de borrador/publicación",
"contentType.kind.change.warning": "Acaba de cambiar el Tipo de Contenido: la API se restablecerá (las rutas, los controladores y los servicios se sobrescribirán).",
"error.attributeName.reserved-name": "Este nombre no se puede utilizar en su Tipo de Contenido, ya que podría romper otras funcionalidades.",
"error.contentType.pluralName-used": "Este valor no puede ser igual al valor singular",

View File

@ -13,8 +13,6 @@
"button.component.create": "Créer un composant",
"button.model.create": "Créer un type de collection",
"button.single-types.create": "Créer un single type",
"contentType.draftAndPublish.description": "Rédigez une version brouillon de chaque entrée avant de la publier",
"contentType.draftAndPublish.label": "Système brouillon/publier",
"contentType.kind.change.warning": "Vous venez de changer le type de ce modèle: L'API va redémarrer (Les routes, controllers, et les services seront écrasés).",
"form.attribute.item.customColumnName": "Nom de colonne personalisée",
"form.attribute.item.customColumnName.description": "Pratique pour renommer la colonne de la db dans un format plus comprehensible pour les responses de l'API",

View File

@ -45,8 +45,6 @@
"contentType.collectionName.description": "Berguna jika nama Jenis Konten dan nama tabel Anda berbeda",
"contentType.collectionName.label": "Nama koleksi",
"contentType.displayName.label": "Nama tampilan",
"contentType.draftAndPublish.description": "Tulis versi draf dari setiap entri sebelum menerbitkannya",
"contentType.draftAndPublish.label": "Draf / sistem terbitkan",
"contentType.kind.change.warning": "Anda baru saja mengubah jenis tipe konten: API akan disetel ulang (rute, pengontrol, dan layanan akan ditimpa).",
"error.attributeName.reserved-name": "Nama ini tidak dapat digunakan dalam tipe konten Anda karena dapat merusak fungsi lainnya",
"error.contentTypeName.reserved-name": "Nama ini tidak dapat digunakan dalam proyek Anda karena dapat merusak fungsi lainnya",

View File

@ -45,8 +45,6 @@
"contentType.collectionName.description": "Utile quando il nome della Collezione differisce dal nome della tabella del DB",
"contentType.collectionName.label": "Nome della Collezione",
"contentType.displayName.label": "Nome visualizzato",
"contentType.draftAndPublish.description": "Scrivi una bozza dell'elemento prima di pubblicarlo",
"contentType.draftAndPublish.label": "Sistema Bozza/pubblicato",
"contentType.kind.change.warning": "Hai cambiato il genere di questo Tipo di Contenuto. Le API verranno ripristinate (route, controller e services verranno sovrascritti).",
"error.attributeName.reserved-name": "Questo nome non può essere utilizzato nel tuo Tipo di Contenuto perché potrebbe danneggiare altre funzionalità",
"error.contentTypeName.reserved-name": "Questo nome non può essere utilizzato nel tuo progetto perché potrebbe danneggiare altre funzionalità",

View File

@ -50,8 +50,6 @@
"contentType.collectionName.description": "콘텐츠 타입과 테이블 이름이 다를 때 유용합니다.",
"contentType.collectionName.label": "콜렉션 이름",
"contentType.displayName.label": "표시 이름",
"contentType.draftAndPublish.description": "각 항목을 발행하기 전에 초안 버전을 작성합니다.",
"contentType.draftAndPublish.label": "초안/발행 시스템",
"contentType.kind.change.warning": "콘텐츠 타입을 수정했습니다. API가 리셋됩니다(라우트, 컨트롤러, 서비스가 덮어씌워집니다.)",
"error.attributeName.reserved-name": "이 이름은 다른 기능을 손상시킬 수 있으므로 사용할 수 없습니다.",
"error.contentType.pluralName-used": "이 값은 단수 이름과 같을 수 없습니다.",

View File

@ -50,8 +50,6 @@
"contentType.collectionName.description": "Przydatne, gdy nazwa typu zawartości i nazwa tabeli różnią się",
"contentType.collectionName.label": "Nazwa kolekcji",
"contentType.displayName.label": "Nazwa",
"contentType.draftAndPublish.description": "Napisz szkic każdego wpisu przed publikacją",
"contentType.draftAndPublish.label": "System szkicu/publikacji",
"contentType.kind.change.warning": "Właśnie zmieniłeś rodzaj typu treści: API zostanie zresetowane (ścieżki, kontrolery i usługi zostaną nadpisane).",
"error.attributeName.reserved-name": "Ta nazwa nie może być używana w tym typie treści, ponieważ może uszkodzić inne funkcje",
"error.contentType.pluralName-used": "Ta wartość nie może być taka sama jak pojedyncza",

Some files were not shown because too many files have changed in this diff Show More