release: 4.23.0 to develop (#20078)

* feat(releases): add release column to CM list view (#19926)

* draft: bulk release injection zone added

* chore: implement cm-api to strapi-app & description component renderer

* feat: bulk delete action

* Revert "draft: bulk release injection zone added"

This reverts commit 2d9e8872e847327888d988009fa16f91b5f1c777.

* feat: bulk unpublish action added

* feat: default bulk publish action

* fix: variable renaming

* fix: on close of modal updated and refetched list on publish action completion

* fix: removed IZ publish and unpublish modals, e2e tests added for default bulk actions

* tests: e2e updated for publish button disabled state

* fix: bug fixed on publishing with already published entries

* fix: e2e tests

* fix: reverted IZ components

* feat: bulk release action added

* update: check right permissions to show add to release button

* update: bulk actions modal updated to accept content(modalbody+modalfooter), publish action and release actions updated accordingly

* fix: types

* test: bulk release e2e test added

* fix: e2e test

* fix: test case timeout added, notification component updated to not saved message

* feat: inject release column in LV

* fix: minor changes

* fix: remove commented code

* fix: upgraded styled components to fix the error: defaultProps circularly references

* feat(content-releases): add to be released in column in list view

* fix: types and 0 releases entry

* fix: invalidate entries in release after bulk release or on deleting a release action

* tests: e2e tests added for release column

* tests: e2e updated to create new release and then add bulk release

* fix: minor changes, test fixed

* fix: delete release action invalite tag added back

* fix: e2e test

* apply josh feedback|

---------

Co-authored-by: Josh <37798644+joshuaellis@users.noreply.github.com>
Co-authored-by: Fernando Chavez <fernando.chavez@strapi.io>

* chore(deps): revert sharp to 0.32.6 (#20066)

* fix: fix boot issue when removing i18n from an app

* chore: get i18n service outside the for loop

* chore: remove empty spaces

* v4.23.0

---------

Co-authored-by: Madhuri Sandbhor <madhurisandbhor@gmail.com>
Co-authored-by: Josh <37798644+joshuaellis@users.noreply.github.com>
Co-authored-by: Fernando Chavez <fernando.chavez@strapi.io>
Co-authored-by: markkaylor <mark.kaylor@strapi.io>
Co-authored-by: Alexandre BODIN <alexandrebodin@users.noreply.github.com>
This commit is contained in:
Marc Roig 2024-04-10 17:08:53 +02:00 committed by GitHub
parent 6a87b376f5
commit 450055e5af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 1103 additions and 747 deletions

View File

@ -1,6 +1,6 @@
{
"name": "check-pr-status",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"license": "MIT",
"main": "dist/index.js",

View File

@ -1,6 +1,6 @@
{
"name": "getstarted",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"description": "A Strapi application.",
"license": "SEE LICENSE IN LICENSE",
@ -14,16 +14,16 @@
},
"dependencies": {
"@strapi/icons": "1.16.0",
"@strapi/plugin-color-picker": "4.22.1",
"@strapi/plugin-documentation": "4.22.1",
"@strapi/plugin-graphql": "4.22.1",
"@strapi/plugin-i18n": "4.22.1",
"@strapi/plugin-sentry": "4.22.1",
"@strapi/plugin-users-permissions": "4.22.1",
"@strapi/provider-email-mailgun": "4.22.1",
"@strapi/provider-upload-aws-s3": "4.22.1",
"@strapi/provider-upload-cloudinary": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/plugin-color-picker": "4.23.0",
"@strapi/plugin-documentation": "4.23.0",
"@strapi/plugin-graphql": "4.23.0",
"@strapi/plugin-i18n": "4.23.0",
"@strapi/plugin-sentry": "4.23.0",
"@strapi/plugin-users-permissions": "4.23.0",
"@strapi/provider-email-mailgun": "4.23.0",
"@strapi/provider-upload-aws-s3": "4.23.0",
"@strapi/provider-upload-cloudinary": "4.23.0",
"@strapi/strapi": "4.23.0",
"better-sqlite3": "8.6.0",
"lodash": "4.17.21",
"mysql": "2.18.1",

View File

@ -1,6 +1,6 @@
{
"name": "kitchensink-ts",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"description": "A Strapi application",
"license": "MIT",
@ -14,9 +14,9 @@
"strapi": "strapi"
},
"dependencies": {
"@strapi/plugin-i18n": "4.22.1",
"@strapi/plugin-users-permissions": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/plugin-i18n": "4.23.0",
"@strapi/plugin-users-permissions": "4.23.0",
"@strapi/strapi": "4.23.0",
"better-sqlite3": "8.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -1,6 +1,6 @@
{
"name": "kitchensink",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"description": "A Strapi application.",
"license": "SEE LICENSE IN LICENSE",
@ -13,10 +13,10 @@
"strapi": "strapi"
},
"dependencies": {
"@strapi/provider-email-mailgun": "4.22.1",
"@strapi/provider-upload-aws-s3": "4.22.1",
"@strapi/provider-upload-cloudinary": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/provider-email-mailgun": "4.23.0",
"@strapi/provider-upload-aws-s3": "4.23.0",
"@strapi/provider-upload-cloudinary": "4.23.0",
"@strapi/strapi": "4.23.0",
"lodash": "4.17.21",
"mysql": "2.18.1",
"mysql2": "3.6.0",

View File

@ -1,6 +1,6 @@
{
"name": "strapi-plugin-workspace-plugin",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"description": "This is the description of my plugin.",
"exports": {

View File

@ -1,5 +1,5 @@
{
"version": "4.22.1",
"version": "4.23.0",
"packages": ["packages/*", "examples/*"],
"npmClient": "yarn",
"useWorkspaces": true,

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/admin-test-utils",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"description": "Test utilities for the Strapi administration panel",
"license": "MIT",
@ -74,8 +74,8 @@
"@reduxjs/toolkit": "1.9.7",
"@strapi/pack-up": "workspace:*",
"@testing-library/jest-dom": "5.16.5",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"peerDependencies": {
"@reduxjs/toolkit": "^1.9.7",

View File

@ -1,6 +1,6 @@
{
"name": "create-strapi-app",
"version": "4.22.1",
"version": "4.23.0",
"description": "Generate a new Strapi application.",
"keywords": [
"create-strapi-app",
@ -43,14 +43,14 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/generate-new": "4.22.1",
"@strapi/generate-new": "4.23.0",
"commander": "8.3.0",
"inquirer": "8.2.5"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "create-strapi-starter",
"version": "4.22.1",
"version": "4.23.0",
"description": "Generate a new Strapi application.",
"keywords": [
"create-strapi-starter",
@ -43,7 +43,7 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/generate-new": "4.22.1",
"@strapi/generate-new": "4.23.0",
"chalk": "4.1.2",
"ci-info": "3.8.0",
"commander": "8.3.0",
@ -53,9 +53,9 @@
"ora": "5.4.1"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -4,6 +4,8 @@ export * from './render';
export type { Store } from './core/store/configure';
export type { SanitizedAdminUser } from '../../shared/contracts/shared';
export type { BulkActionComponent } from './core/apis/content-manager';
export type { CMAdminConfiguration } from './types/adminConfiguration';
export type { ListLayoutRow } from './content-manager/utils/layouts';
export { useDocument as unstable_useDocument } from './hooks/useDocument';
// TODO: Replace this export with the same hook exported from the @strapi/admin/strapi-admin/ee in another iteration of this solution

View File

@ -0,0 +1,107 @@
import type { Attribute, Schema } from '@strapi/types';
/* -------------------------------------------------------------------------------------------------
* Configuration This should be exported from the Content Manager plugin.
* -----------------------------------------------------------------------------------------------*/
export interface Settings {
bulkable: boolean;
filterable: boolean;
searchable: boolean;
pageSize: number;
mainField: string;
defaultSortBy: string;
defaultSortOrder: string;
}
export interface Metadatas {
[key: string]: {
edit:
| {
label: string;
description: string;
placeholder: string;
visible: boolean;
editable: boolean;
}
| object;
list:
| {
label: string;
searchable: boolean;
sortable: boolean;
}
| object;
};
}
export interface Layouts {
list: string[];
edit: { name: string; size: number }[][];
}
export interface Configuration {
uid: string;
settings: Settings;
metadatas: Metadatas;
layouts: Layouts;
}
/* -------------------------------------------------------------------------------------------------
* CMAdminConfiguration
* -----------------------------------------------------------------------------------------------*/
/**
* The admin panel completely mutates the configuration object before passing it anywhere.
* So unfortunately, we need to create a special type here. It looks like it just smashes
* a configuration and schema together...
*
* In the future we could look at fixing this and making it make some sense, but that
* would be a breaking change. But perhaps necessary?
*/
export interface CMAdminConfiguration
extends Omit<Configuration, 'layouts'>,
Omit<Schema.ContentType, 'uid' | 'collectionName' | 'globalId' | 'modelName'> {
apiID: string;
isDisplayed: boolean;
layouts: {
list: null;
edit: Array<RelationLayout | NonRelationLayout>[];
};
}
export type NonRelationLayout = Layouts['edit'][number][number] & {
fieldSchema: Pick<Exclude<Attribute.Any, { type: 'relation' }>, 'pluginOptions' | 'type'>;
/**
* why is this trying to beplural? You don't pluralize metadata.
*
* TODO: does this object come from somewhere else in the codebase?
*/
metadatas: {
description: string;
editable: boolean;
label: string;
placeholder: string;
visible: boolean;
};
};
export interface RelationLayout extends Omit<NonRelationLayout, 'fieldSchema'> {
fieldSchema: Pick<
Extract<Attribute.Any, { type: 'relation' }>,
'pluginOptions' | 'relation' | 'type'
> & {
mappedBy: string;
relationType: string;
target: string;
targetModel: string;
};
queryInfos: {
shouldDisplayRelationLink: boolean;
defaultParams: {
locale?: string;
[key: string]: string | undefined;
};
};
targetModelPluginOptions: object;
}

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/admin",
"version": "4.22.1",
"version": "4.23.0",
"description": "Strapi Admin",
"repository": {
"type": "git",
@ -73,13 +73,13 @@
"@radix-ui/react-toolbar": "1.0.4",
"@reduxjs/toolkit": "1.9.7",
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/permissions": "4.22.1",
"@strapi/provider-audit-logs-local": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/typescript-utils": "4.22.1",
"@strapi/utils": "4.22.1",
"@strapi/permissions": "4.23.0",
"@strapi/provider-audit-logs-local": "4.23.0",
"@strapi/types": "4.23.0",
"@strapi/typescript-utils": "4.23.0",
"@strapi/utils": "4.23.0",
"@vitejs/plugin-react-swc": "3.5.0",
"axios": "1.6.0",
"bcryptjs": "2.4.3",
@ -169,11 +169,11 @@
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/admin-test-utils": "4.22.1",
"@strapi/data-transfer": "4.22.1",
"@strapi/pack-up": "4.22.1",
"@strapi/plugin-content-manager": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/admin-test-utils": "4.23.0",
"@strapi/data-transfer": "4.23.0",
"@strapi/pack-up": "4.23.0",
"@strapi/plugin-content-manager": "4.23.0",
"@strapi/strapi": "4.23.0",
"@testing-library/dom": "9.2.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-content-manager",
"version": "4.22.1",
"version": "4.23.0",
"description": "A powerful UI to easily manage your data.",
"repository": {
"type": "git",
@ -49,8 +49,8 @@
},
"dependencies": {
"@sindresorhus/slugify": "1.1.0",
"@strapi/types": "4.22.1",
"@strapi/utils": "4.22.1",
"@strapi/types": "4.23.0",
"@strapi/utils": "4.23.0",
"koa": "2.13.4",
"koa-bodyparser": "4.4.1",
"lodash": "4.17.21",

View File

@ -329,7 +329,7 @@ export const CMReleasesContainer = () => {
alignItems="start"
borderWidth="1px"
borderStyle="solid"
borderColor={getReleaseColorVariant(release.action.type, '200')}
borderColor={getReleaseColorVariant(release.actions[0].type, '200')}
overflow="hidden"
hasRadius
>
@ -338,13 +338,13 @@ export const CMReleasesContainer = () => {
paddingBottom={3}
paddingLeft={4}
paddingRight={4}
background={getReleaseColorVariant(release.action.type, '100')}
background={getReleaseColorVariant(release.actions[0].type, '100')}
width="100%"
>
<Typography
fontSize={1}
variant="pi"
textColor={getReleaseColorVariant(release.action.type, '600')}
textColor={getReleaseColorVariant(release.actions[0].type, '600')}
>
{formatMessage(
{
@ -352,7 +352,7 @@ export const CMReleasesContainer = () => {
defaultMessage:
'{isPublish, select, true {Will be published in} other {Will be unpublished in}}',
},
{ isPublish: release.action.type === 'publish' }
{ isPublish: release.actions[0].type === 'publish' }
)}
</Typography>
</Box>
@ -391,7 +391,7 @@ export const CMReleasesContainer = () => {
<ReleaseActionMenu.EditReleaseItem releaseId={release.id} />
<ReleaseActionMenu.DeleteReleaseActionItem
releaseId={release.id}
actionId={release.action.id}
actionId={release.actions[0].id}
/>
</ReleaseActionMenu.Root>
</CheckPermissions>

View File

@ -0,0 +1,164 @@
import * as React from 'react';
import { Box, Flex, Popover, Typography } from '@strapi/design-system';
import { Link } from '@strapi/design-system/v2';
import { SortIcon } from '@strapi/helper-plugin';
import { EntityService, Common } from '@strapi/types';
import { useIntl } from 'react-intl';
import styled from 'styled-components';
import { useGetMappedEntriesInReleasesQuery } from '../services/release';
import { useTypedSelector } from '../store/hooks';
import type { CMAdminConfiguration, ListLayoutRow } from '@strapi/admin/strapi-admin';
type Entity = EntityService.Result<Common.UID.Schema>;
const Button = styled.button`
svg {
> g,
path {
fill: ${({ theme }) => theme.colors.neutral500};
}
}
&:hover {
svg {
> g,
path {
fill: ${({ theme }) => theme.colors.neutral600};
}
}
}
&:active {
svg {
> g,
path {
fill: ${({ theme }) => theme.colors.neutral400};
}
}
}
`;
const ActionWrapper = styled(Flex)`
svg {
height: ${4 / 16}rem;
}
`;
/* -------------------------------------------------------------------------------------------------
* useReleasesList
* -----------------------------------------------------------------------------------------------*/
const useReleasesList = (entryId: string | number) => {
// @ts-expect-error TODO: fix Property 'uid' does not exist on type 'FormattedContentTypeLayout | null'
const { uid: contentTypeUid } = useTypedSelector(
(state) => state['content-manager_listView'].contentType
);
const listViewData = useTypedSelector((state) => state['content-manager_listView'].data);
const entriesIds = listViewData.map((entry) => entry.id);
const response = useGetMappedEntriesInReleasesQuery(
{ contentTypeUid, entriesIds },
{ skip: !entriesIds || !contentTypeUid || entriesIds.length === 0 }
);
const mappedEntriesInReleases = response.data || {};
return mappedEntriesInReleases?.[entryId] || [];
};
/* -------------------------------------------------------------------------------------------------
* addColumnToTableHook
* -----------------------------------------------------------------------------------------------*/
interface AddColumnToTableHookArgs {
layout: {
components: Record<string, CMAdminConfiguration>;
contentType: CMAdminConfiguration;
};
displayedHeaders: ListLayoutRow[];
}
const addColumnToTableHook = ({ displayedHeaders, layout }: AddColumnToTableHookArgs) => {
const { contentType } = layout;
if (!contentType.options?.draftAndPublish) {
return { displayedHeaders, layout };
}
return {
displayedHeaders: [
...displayedHeaders,
{
key: '__release_key__',
fieldSchema: { type: 'string' },
metadatas: { label: 'To be released in', searchable: true, sortable: false },
name: 'releasedAt',
cellFormatter: (props: Entity) => <ReleaseListCell {...props} />,
},
],
layout,
};
};
/* -------------------------------------------------------------------------------------------------
* ReleaseListCell
* -----------------------------------------------------------------------------------------------*/
interface ReleaseListCellProps {
id: Entity['id'];
}
const ReleaseListCell = ({ id }: ReleaseListCellProps) => {
const releases = useReleasesList(id);
const [visible, setVisible] = React.useState(false);
const buttonRef = React.useRef<HTMLButtonElement>(null);
const { formatMessage } = useIntl();
const handleTogglePopover = () => setVisible((prev) => !prev);
return (
<Flex onClick={(e) => e.stopPropagation()}>
<Button type="button" onClick={handleTogglePopover} ref={buttonRef}>
<ActionWrapper height="2rem" width="2rem">
<Typography style={{ maxWidth: '252px', cursor: 'pointer' }} textColor="neutral800">
{releases.length > 0
? formatMessage(
{
id: 'content-releases.content-manager.list-view.releases-number',
defaultMessage: '{number} {number, plural, one {release} other {releases}}',
},
{
number: releases.length,
}
)
: '-'}
</Typography>
<Flex>
{releases.length > 0 && <SortIcon />}
{visible && (
<Popover
onDismiss={handleTogglePopover}
source={buttonRef as React.MutableRefObject<HTMLElement>}
spacing={16}
>
<ul>
{releases.map(({ id, name }) => (
<Box key={id} padding={3} as="li">
<Link href={`/admin/plugins/content-releases/${id}`} isExternal={false}>
{name}
</Link>
</Box>
))}
</ul>
</Popover>
)}
</Flex>
</ActionWrapper>
</Button>
</Flex>
);
};
export { ReleaseListCell, addColumnToTableHook };
export type { ReleaseListCellProps };

View File

@ -108,7 +108,7 @@ describe('CMReleasesContainer', () => {
rest.get('/content-releases', (req, res, ctx) => {
return res(
ctx.json({
data: [{ name: 'release1', id: '1', action: { type: 'publish' } }],
data: [{ name: 'release1', id: '1', actions: [{ type: 'publish' }] }],
})
);
})
@ -135,8 +135,8 @@ describe('CMReleasesContainer', () => {
return res(
ctx.json({
data: [
{ name: 'release1', id: '1', action: { type: 'publish' } },
{ name: 'release2', id: '2', action: { type: 'unpublish' } },
{ name: 'release1', id: '1', actions: [{ type: 'publish' }] },
{ name: 'release2', id: '2', actions: [{ type: 'unpublish' }] },
],
})
);
@ -162,7 +162,7 @@ describe('CMReleasesContainer', () => {
{
name: 'release1',
id: '1',
action: { type: 'publish' },
actions: [{ type: 'publish' }],
scheduledAt: '2024-01-01T10:00:00.000Z',
timezone: 'Europe/Paris',
},

View File

@ -3,6 +3,7 @@ import { PaperPlane } from '@strapi/icons';
import { CMReleasesContainer } from './components/CMReleasesContainer';
import { ReleaseAction } from './components/ReleaseAction';
import { addColumnToTableHook } from './components/ReleaseListCell';
import { PERMISSIONS } from './constants';
import { pluginId } from './pluginId';
import { releaseApi } from './services/release';
@ -58,6 +59,8 @@ const admin: Plugin.Config.AdminInput = {
actions.splice(deleteActionIndex, 0, ReleaseAction);
return actions;
});
// Hook that adds a column into the CM's LV table
app.registerHook('Admin/CM/pages/ListView/inject-column-in-table', addColumnToTableHook);
} else if (
!window.strapi.features.isEnabled('cms-content-releases') &&
window.strapi?.flags?.promoteEE

View File

@ -22,6 +22,7 @@ import type {
UpdateRelease,
GetRelease,
PublishRelease,
MapEntriesToReleases,
} from '../../../shared/contracts/releases';
export interface GetReleasesQueryParams {
@ -50,7 +51,7 @@ type GetReleasesTabResponse = GetReleases.Response & {
const releaseApi = createApi({
reducerPath: pluginId,
baseQuery: axiosBaseQuery,
tagTypes: ['Release', 'ReleaseAction'],
tagTypes: ['Release', 'ReleaseAction', 'EntriesInRelease'],
endpoints: (build) => {
return {
getReleasesForEntry: build.query<
@ -200,6 +201,7 @@ const releaseApi = createApi({
invalidatesTags: [
{ type: 'Release', id: 'LIST' },
{ type: 'ReleaseAction', id: 'LIST' },
{ type: 'EntriesInRelease' },
],
}),
updateReleaseAction: build.mutation<
@ -255,6 +257,7 @@ const releaseApi = createApi({
{ type: 'Release', id: 'LIST' },
{ type: 'Release', id: arg.params.releaseId },
{ type: 'ReleaseAction', id: 'LIST' },
{ type: 'EntriesInRelease' },
],
}),
publishRelease: build.mutation<PublishRelease.Response, PublishRelease.Request['params']>({
@ -273,7 +276,25 @@ const releaseApi = createApi({
method: 'DELETE',
};
},
invalidatesTags: () => [{ type: 'Release', id: 'LIST' }],
invalidatesTags: () => [{ type: 'Release', id: 'LIST' }, { type: 'EntriesInRelease' }],
}),
getMappedEntriesInReleases: build.query<
MapEntriesToReleases.Response['data'],
MapEntriesToReleases.Request['query']
>({
query(params) {
return {
url: '/content-releases/mapEntriesToReleases',
method: 'GET',
config: {
params,
},
};
},
transformResponse(response: MapEntriesToReleases.Response) {
return response.data;
},
providesTags: [{ type: 'EntriesInRelease' }],
}),
};
},
@ -292,6 +313,7 @@ const {
usePublishReleaseMutation,
useDeleteReleaseActionMutation,
useDeleteReleaseMutation,
useGetMappedEntriesInReleasesQuery,
} = releaseApi;
export {
@ -307,5 +329,6 @@ export {
usePublishReleaseMutation,
useDeleteReleaseActionMutation,
useDeleteReleaseMutation,
useGetMappedEntriesInReleasesQuery,
releaseApi,
};

View File

@ -24,6 +24,7 @@
"content-manager-list-view.add-to-release.notification.success.title": "Successfully added to release.",
"content-manager-list-view.add-to-release.notification.success.message": "{entriesAlreadyInRelease} out of {totalEntries} entries were already in the release.",
"content-manager.notification.entry-error": "Failed to get entry data",
"content-manager.list-view.releases-number": "{number} {number, plural, one {release} other {releases}}",
"plugin.name": "Releases",
"pages.Releases.title": "Releases",
"pages.Releases.header-subtitle": "Create and manage content updates",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/content-releases",
"version": "4.22.1",
"version": "4.23.0",
"description": "Strapi plugin for organizing and releasing content",
"repository": {
"type": "git",
@ -56,10 +56,10 @@
"dependencies": {
"@reduxjs/toolkit": "1.9.7",
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/types": "workspace:*",
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"axios": "1.6.0",
"date-fns": "2.30.0",
"date-fns-tz": "2.0.0",
@ -71,10 +71,10 @@
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/admin": "4.22.1",
"@strapi/admin-test-utils": "4.22.1",
"@strapi/admin": "4.23.0",
"@strapi/admin-test-utils": "4.23.0",
"@strapi/pack-up": "workspace:*",
"@strapi/strapi": "4.22.1",
"@strapi/strapi": "4.23.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",
"@types/koa": "2.13.4",

View File

@ -267,4 +267,85 @@ describe('Release controller', () => {
expect(strapi.admin.services.user.sanitizeUser).toHaveBeenCalled();
});
});
describe('mapEntriesToReleases', () => {
it('should throw an error if contentTypeUid or entriesIds are missing', async () => {
const ctx = {
query: {},
};
// @ts-expect-error partial context
await expect(() => releaseController.mapEntriesToReleases(ctx)).rejects.toThrow(
'Missing required query parameters'
);
});
it('should call findManyWithContentTypeEntryAttached with correct parameters', async () => {
const ctx = {
query: {
contentTypeUid: 'api::kitchensink.kitchensink',
entriesIds: [1, 2, 3],
},
};
const mockRelease = {
id: 1,
name: 'Test Release',
actions: [
{
entry: {
id: 1,
},
},
],
};
mockFindManyWithContentTypeEntryAttached.mockResolvedValue([mockRelease]);
// @ts-expect-error partial context
await releaseController.mapEntriesToReleases(ctx);
expect(mockFindManyWithContentTypeEntryAttached).toHaveBeenCalledWith(
'api::kitchensink.kitchensink',
[1, 2, 3]
);
});
it('should map entries to releases correctly', async () => {
const ctx = {
query: {
contentTypeUid: 'api::kitchensink.kitchensink',
entriesIds: [1, 2, 3],
},
body: { data: {} },
};
const mockRelease = {
id: 1,
name: 'Test Release',
actions: [
{
entry: {
id: 1,
},
},
{
entry: {
id: 2,
},
},
],
};
mockFindManyWithContentTypeEntryAttached.mockResolvedValue([mockRelease]);
// @ts-expect-error partial context
await releaseController.mapEntriesToReleases(ctx);
expect(ctx.body.data).toEqual({
1: [{ id: 1, name: 'Test Release' }],
2: [{ id: 1, name: 'Test Release' }],
});
});
});
});

View File

@ -11,6 +11,7 @@ import type {
DeleteRelease,
GetContentTypeEntryReleases,
GetReleases,
MapEntriesToReleases,
} from '../../../shared/contracts/releases';
import type { UserInfo } from '../../../shared/types';
import { getService } from '../utils';
@ -106,6 +107,40 @@ const releaseController = {
ctx.body = { data };
},
async mapEntriesToReleases(ctx: Koa.Context) {
const { contentTypeUid, entriesIds } = ctx.query;
if (!contentTypeUid || !entriesIds) {
throw new errors.ValidationError('Missing required query parameters');
}
const releaseService = getService('release', { strapi });
const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
contentTypeUid,
entriesIds
);
const mappedEntriesInReleases = releasesWithActions.reduce(
(acc: MapEntriesToReleases.Response['data']['mappedEntriesInReleases'], release: Release) => {
release.actions.forEach((action) => {
if (!acc[action.entry.id]) {
acc[action.entry.id] = [{ id: release.id, name: release.name }];
} else {
acc[action.entry.id].push({ id: release.id, name: release.name });
}
});
return acc;
},
{} as MapEntriesToReleases.Response['data']['mappedEntriesInReleases']
);
ctx.body = {
data: mappedEntriesInReleases,
};
},
async create(ctx: Koa.Context) {
const user: UserInfo = ctx.state.user;
const releaseArgs: CreateRelease.Request['body'] = ctx.request.body;

View File

@ -185,6 +185,11 @@ export async function disableContentTypeLocalized({ oldContentTypes, contentType
return;
}
const i18nPlugin = strapi.plugin('i18n');
if (!i18nPlugin) {
return;
}
for (const uid in contentTypes) {
if (!oldContentTypes[uid]) {
continue;
@ -193,7 +198,6 @@ export async function disableContentTypeLocalized({ oldContentTypes, contentType
const oldContentType = oldContentTypes[uid];
const contentType = contentTypes[uid];
const i18nPlugin = strapi.plugin('i18n');
const { isLocalizedContentType } = i18nPlugin.service('content-types');
// if i18N is disabled remove non default locales before sync
@ -214,6 +218,11 @@ export async function enableContentTypeLocalized({ oldContentTypes, contentTypes
return;
}
const i18nPlugin = strapi.plugin('i18n');
if (!i18nPlugin) {
return;
}
for (const uid in contentTypes) {
if (!oldContentTypes[uid]) {
continue;
@ -222,7 +231,6 @@ export async function enableContentTypeLocalized({ oldContentTypes, contentTypes
const oldContentType = oldContentTypes[uid];
const contentType = contentTypes[uid];
const i18nPlugin = strapi.plugin('i18n');
const { isLocalizedContentType } = i18nPlugin.service('content-types');
const { getDefaultLocale } = i18nPlugin.service('locales');

View File

@ -1,6 +1,22 @@
export default {
type: 'admin',
routes: [
{
method: 'GET',
path: '/mapEntriesToReleases',
handler: 'release.mapEntriesToReleases',
config: {
policies: [
'admin::isAuthenticatedAdmin',
{
name: 'admin::hasPermissions',
config: {
actions: ['plugin::content-releases.read'],
},
},
],
},
},
{
method: 'POST',
path: '/',

View File

@ -481,7 +481,7 @@ describe('release service', () => {
1
);
expect(releases).toEqual([{ name: 'test release', action: { type: 'publish' } }]);
expect(releases).toEqual([{ name: 'test release', actions: [{ type: 'publish' }] }]);
});
});

View File

@ -14,6 +14,7 @@ import type {
Release,
DeleteRelease,
GetContentTypeEntryReleases,
MapEntriesToReleases,
} from '../../../shared/contracts/releases';
import type {
CreateReleaseAction,
@ -257,13 +258,23 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => {
async findManyWithContentTypeEntryAttached(
contentTypeUid: GetContentTypeEntryReleases.Request['query']['contentTypeUid'],
entryId: GetContentTypeEntryReleases.Request['query']['entryId']
entriesIds:
| GetContentTypeEntryReleases.Request['query']['entryId']
| MapEntriesToReleases.Request['query']['entriesIds']
) {
// entriesIds could be an array or a single value
let entries = entriesIds;
if (!Array.isArray(entriesIds)) {
entries = [entriesIds];
}
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
where: {
actions: {
target_type: contentTypeUid,
target_id: entryId,
target_id: {
$in: entries,
},
},
releasedAt: {
$null: true,
@ -274,7 +285,14 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => {
actions: {
where: {
target_type: contentTypeUid,
target_id: entryId,
target_id: {
$in: entries,
},
},
populate: {
entry: {
select: ['id'],
},
},
},
},
@ -282,14 +300,14 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => {
return releases.map((release) => {
if (release.actions?.length) {
const [actionForEntry] = release.actions;
const actionsForEntry = release.actions;
// Remove the actions key to replace it with an action key
delete release.actions;
return {
...release,
action: actionForEntry,
actions: actionsForEntry,
};
}

View File

@ -27,7 +27,7 @@ export interface ReleaseDataResponse extends Omit<Release, 'actions'> {
}
export interface ReleaseForContentTypeEntryDataResponse extends Omit<Release, 'actions'> {
action: ReleaseAction;
actions: ReleaseAction[];
}
/**
@ -72,6 +72,24 @@ export declare namespace GetContentTypeEntryReleases {
}
}
/**
* GET /content-releases/mapEntriesToReleases - Map entries to releases
*/
export declare namespace MapEntriesToReleases {
export interface Request {
query: {
contentTypeUid: ReleaseAction['contentType'];
entriesIds: ReleaseAction['entry']['id'][];
};
}
export interface Response {
data: {
[entryId: ReleaseAction['entry']['id']]: Pick<Release, 'id' | 'name'>[];
};
}
}
/**
* GET /content-releases/:id - Get a single release
*/

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-content-type-builder",
"version": "4.22.1",
"version": "4.23.0",
"description": "Strapi plugin to create content type",
"repository": {
"type": "git",
@ -59,10 +59,10 @@
"@reduxjs/toolkit": "1.9.7",
"@sindresorhus/slugify": "1.1.0",
"@strapi/design-system": "1.16.0",
"@strapi/generators": "4.22.1",
"@strapi/helper-plugin": "4.22.1",
"@strapi/generators": "4.23.0",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"fs-extra": "10.0.0",
"immer": "9.0.19",
"koa-bodyparser": "4.4.1",
@ -76,9 +76,9 @@
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/strapi": "4.23.0",
"@strapi/types": "4.23.0",
"@testing-library/react": "14.0.0",
"@testing-library/react-hooks": "8.0.1",
"@testing-library/user-event": "14.4.3",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/data-transfer",
"version": "4.22.1",
"version": "4.23.0",
"description": "Data transfer capabilities for Strapi",
"keywords": [
"strapi",
@ -40,10 +40,10 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/logger": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/utils": "4.22.1",
"@strapi/logger": "4.23.0",
"@strapi/strapi": "4.23.0",
"@strapi/types": "4.23.0",
"@strapi/utils": "4.23.0",
"chalk": "4.1.2",
"cli-table3": "0.6.2",
"commander": "8.3.0",
@ -60,7 +60,7 @@
"ws": "8.13.0"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@types/fs-extra": "9.0.13",
"@types/jest": "29.5.2",
"@types/koa": "2.13.4",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/database",
"version": "4.22.1",
"version": "4.23.0",
"description": "Strapi's database layer",
"homepage": "https://strapi.io",
"bugs": {
@ -39,7 +39,7 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"date-fns": "2.30.0",
"debug": "4.3.4",
"fs-extra": "10.0.0",
@ -49,9 +49,9 @@
"umzug": "3.2.1"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-email",
"version": "4.22.1",
"version": "4.23.0",
"description": "Easily configure your Strapi application to send emails.",
"repository": {
"type": "git",
@ -53,10 +53,10 @@
},
"dependencies": {
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/provider-email-sendmail": "4.22.1",
"@strapi/utils": "4.22.1",
"@strapi/provider-email-sendmail": "4.23.0",
"@strapi/utils": "4.23.0",
"lodash": "4.17.21",
"prop-types": "^15.8.1",
"react-intl": "6.4.1",
@ -64,8 +64,8 @@
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/types": "4.23.0",
"@testing-library/react": "14.0.0",
"@types/koa": "2.13.4",
"@types/lodash": "^4.14.191",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/helper-plugin",
"version": "4.22.1",
"version": "4.23.0",
"description": "Helper for Strapi plugins development",
"repository": {
"type": "git",
@ -68,15 +68,15 @@
"@storybook/addon-mdx-gfm": "7.5.3",
"@storybook/builder-vite": "7.5.3",
"@storybook/react-vite": "7.5.3",
"@strapi/admin-test-utils": "4.22.1",
"@strapi/admin-test-utils": "4.23.0",
"@strapi/design-system": "1.16.0",
"@strapi/icons": "1.16.0",
"@strapi/pack-up": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/types": "4.23.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",
"cross-env": "^7.0.3",
"eslint-config-custom": "4.22.1",
"eslint-config-custom": "4.23.0",
"eslint-plugin-storybook": "0.6.14",
"msw": "1.3.0",
"react": "^18.2.0",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/permissions",
"version": "4.22.1",
"version": "4.23.0",
"description": "Strapi's permission layer.",
"repository": {
"type": "git",
@ -37,15 +37,15 @@
},
"dependencies": {
"@casl/ability": "6.5.0",
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"lodash": "4.17.21",
"qs": "6.11.1",
"sift": "16.0.1"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/strapi",
"version": "4.22.1",
"version": "4.23.0",
"description": "An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite",
"keywords": [
"strapi",
@ -113,22 +113,22 @@
"dependencies": {
"@koa/cors": "3.4.3",
"@koa/router": "10.1.1",
"@strapi/admin": "4.22.1",
"@strapi/content-releases": "4.22.1",
"@strapi/data-transfer": "4.22.1",
"@strapi/database": "4.22.1",
"@strapi/generate-new": "4.22.1",
"@strapi/generators": "4.22.1",
"@strapi/logger": "4.22.1",
"@strapi/pack-up": "4.22.1",
"@strapi/permissions": "4.22.1",
"@strapi/plugin-content-manager": "4.22.1",
"@strapi/plugin-content-type-builder": "4.22.1",
"@strapi/plugin-email": "4.22.1",
"@strapi/plugin-upload": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/typescript-utils": "4.22.1",
"@strapi/utils": "4.22.1",
"@strapi/admin": "4.23.0",
"@strapi/content-releases": "4.23.0",
"@strapi/data-transfer": "4.23.0",
"@strapi/database": "4.23.0",
"@strapi/generate-new": "4.23.0",
"@strapi/generators": "4.23.0",
"@strapi/logger": "4.23.0",
"@strapi/pack-up": "4.23.0",
"@strapi/permissions": "4.23.0",
"@strapi/plugin-content-manager": "4.23.0",
"@strapi/plugin-content-type-builder": "4.23.0",
"@strapi/plugin-email": "4.23.0",
"@strapi/plugin-upload": "4.23.0",
"@strapi/types": "4.23.0",
"@strapi/typescript-utils": "4.23.0",
"@strapi/utils": "4.23.0",
"bcryptjs": "2.4.3",
"boxen": "5.1.2",
"chalk": "4.1.2",
@ -195,9 +195,9 @@
"@types/node-schedule": "2.1.0",
"@types/nodemon": "1.19.6",
"@types/statuses": "2.0.1",
"eslint-config-custom": "4.22.1",
"eslint-config-custom": "4.23.0",
"supertest": "6.3.3",
"tsconfig": "4.22.1"
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/types",
"version": "4.22.1",
"version": "4.23.0",
"description": "Shared typescript types for Strapi internal use",
"keywords": [
"strapi"
@ -46,10 +46,10 @@
"@casl/ability": "6.5.0",
"@koa/cors": "3.4.3",
"@koa/router": "10.1.1",
"@strapi/database": "4.22.1",
"@strapi/logger": "4.22.1",
"@strapi/permissions": "4.22.1",
"@strapi/utils": "4.22.1",
"@strapi/database": "4.23.0",
"@strapi/logger": "4.23.0",
"@strapi/permissions": "4.23.0",
"@strapi/utils": "4.23.0",
"commander": "8.3.0",
"https-proxy-agent": "5.0.1",
"koa": "2.13.4",
@ -57,14 +57,14 @@
"node-schedule": "2.1.0"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/ts-zen": "^0.2.0",
"@types/jest": "29.5.2",
"@types/koa": "2.13.4",
"@types/koa__router": "12.0.0",
"@types/node-schedule": "2.1.0",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0",
"typescript": "5.2.2"
},
"engines": {

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-upload",
"version": "4.22.1",
"version": "4.23.0",
"description": "Makes it easy to upload images and files to your Strapi Application.",
"license": "SEE LICENSE IN LICENSE",
"author": {
@ -43,10 +43,10 @@
},
"dependencies": {
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/provider-upload-local": "4.22.1",
"@strapi/utils": "4.22.1",
"@strapi/provider-upload-local": "4.23.0",
"@strapi/utils": "4.23.0",
"axios": "1.6.0",
"byte-size": "7.0.1",
"cropperjs": "1.6.0",
@ -66,12 +66,12 @@
"react-query": "3.39.3",
"react-redux": "8.1.1",
"react-select": "5.7.0",
"sharp": "0.33.3",
"sharp": "0.32.6",
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/strapi": "4.23.0",
"@testing-library/dom": "9.2.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/utils",
"version": "4.22.1",
"version": "4.23.0",
"description": "Shared utilities for the Strapi packages",
"keywords": [
"strapi",
@ -52,14 +52,14 @@
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/types": "4.23.0",
"@types/koa": "2.13.4",
"@types/node": "18.18.4",
"eslint-config-custom": "4.22.1",
"eslint-config-custom": "4.23.0",
"koa": "2.13.4",
"koa-body": "4.2.0",
"tsconfig": "4.22.1"
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/generate-new",
"version": "4.22.1",
"version": "4.23.0",
"description": "Generate a new Strapi application.",
"keywords": [
"generate",
@ -57,7 +57,7 @@
"tar": "6.1.13"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/pack-up": "4.23.0",
"copyfiles": "2.4.1"
},
"engines": {

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/generators",
"version": "4.22.1",
"version": "4.23.0",
"description": "Interactive API generator.",
"keywords": [
"strapi",
@ -46,8 +46,8 @@
},
"dependencies": {
"@sindresorhus/slugify": "1.1.0",
"@strapi/typescript-utils": "4.22.1",
"@strapi/utils": "4.22.1",
"@strapi/typescript-utils": "4.23.0",
"@strapi/utils": "4.23.0",
"chalk": "4.1.2",
"copyfiles": "2.4.1",
"fs-extra": "10.0.0",
@ -56,9 +56,9 @@
"pluralize": "8.0.0"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-cloud",
"version": "4.22.1",
"version": "4.23.0",
"description": "Instructions to deploy your local project to Strapi Cloud",
"license": "MIT",
"author": {
@ -37,22 +37,22 @@
},
"dependencies": {
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"react-intl": "6.4.1"
},
"devDependencies": {
"@strapi/strapi": "4.22.1",
"@strapi/strapi": "4.23.0",
"@types/react": "18.2.39",
"@types/react-dom": "18.2.17",
"@types/react-router-dom": "^5.3.3",
"@types/styled-components": "5.1.32",
"eslint-config-custom": "4.22.1",
"eslint-config-custom": "4.23.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router-dom": "5.3.4",
"styled-components": "5.3.3",
"tsconfig": "4.22.1",
"tsconfig": "4.23.0",
"typescript": "5.2.2"
},
"peerDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-color-picker",
"version": "4.22.1",
"version": "4.23.0",
"description": "Strapi maintained Custom Fields",
"repository": {
"type": "git",
@ -54,14 +54,14 @@
},
"dependencies": {
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"prop-types": "^15.8.1",
"react-colorful": "5.6.1",
"react-intl": "6.4.1"
},
"devDependencies": {
"@strapi/strapi": "4.22.1",
"@strapi/strapi": "4.23.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",
"@types/styled-components": "5.1.32",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-documentation",
"version": "4.22.1",
"version": "4.23.0",
"description": "Create an OpenAPI Document and visualize your API with SWAGGER UI.",
"repository": {
"type": "git",
@ -46,9 +46,9 @@
},
"dependencies": {
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"bcryptjs": "2.4.3",
"cheerio": "^1.0.0-rc.12",
"formik": "2.4.0",
@ -66,8 +66,8 @@
},
"devDependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@strapi/pack-up": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/strapi": "4.23.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",
"msw": "1.3.0",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-graphql",
"version": "4.22.1",
"version": "4.23.0",
"description": "Adds GraphQL endpoint with default API methods.",
"repository": {
"type": "git",
@ -51,9 +51,9 @@
"@graphql-tools/schema": "8.5.1",
"@graphql-tools/utils": "^8.13.1",
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"apollo-server-core": "3.12.1",
"apollo-server-koa": "3.10.0",
"graphql": "^15.5.1",
@ -67,18 +67,18 @@
"pluralize": "8.0.0"
},
"devDependencies": {
"@strapi/strapi": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/strapi": "4.23.0",
"@strapi/types": "4.23.0",
"@types/graphql-depth-limit": "1.1.5",
"@types/graphql-upload": "8.0.12",
"cross-env": "^7.0.3",
"eslint-config-custom": "4.22.1",
"eslint-config-custom": "4.23.0",
"koa": "2.13.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "5.3.4",
"styled-components": "5.3.3",
"tsconfig": "4.22.1",
"tsconfig": "4.23.0",
"typescript": "5.2.2"
},
"peerDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-i18n",
"version": "4.22.1",
"version": "4.23.0",
"description": "This plugin enables to create, to read and to update content in different languages, both from the Admin Panel and from the API",
"repository": {
"type": "git",
@ -55,9 +55,9 @@
"dependencies": {
"@reduxjs/toolkit": "1.9.7",
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"axios": "1.6.0",
"formik": "2.4.0",
"immer": "9.0.19",
@ -70,10 +70,10 @@
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/admin-test-utils": "4.22.1",
"@strapi/pack-up": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/types": "4.22.1",
"@strapi/admin-test-utils": "4.23.0",
"@strapi/pack-up": "4.23.0",
"@strapi/strapi": "4.23.0",
"@strapi/types": "4.23.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",
"msw": "1.3.0",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-sentry",
"version": "4.22.1",
"version": "4.23.0",
"description": "Send Strapi error events to Sentry",
"repository": {
"type": "git",
@ -52,12 +52,12 @@
"dependencies": {
"@sentry/node": "6.19.7",
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/strapi": "4.23.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "5.3.4",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/plugin-users-permissions",
"version": "4.22.1",
"version": "4.23.0",
"description": "Protect your API with a full-authentication process based on JWT",
"repository": {
"type": "git",
@ -47,9 +47,9 @@
},
"dependencies": {
"@strapi/design-system": "1.16.0",
"@strapi/helper-plugin": "4.22.1",
"@strapi/helper-plugin": "4.23.0",
"@strapi/icons": "1.16.0",
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"bcryptjs": "2.4.3",
"formik": "2.4.0",
"grant-koa": "5.4.8",
@ -68,8 +68,8 @@
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/strapi": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@strapi/strapi": "4.23.0",
"@testing-library/dom": "9.2.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-audit-logs-local",
"version": "4.22.1",
"version": "4.23.0",
"description": "Local provider for strapi audit logs",
"keywords": [
"audit-logs",
@ -41,10 +41,10 @@
"watch": "pack-up watch"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/types": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"@strapi/types": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-email-amazon-ses",
"version": "4.22.1",
"version": "4.23.0",
"description": "Amazon SES provider for strapi email",
"keywords": [
"email",
@ -42,13 +42,13 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"node-ses": "^3.0.3"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-email-mailgun",
"version": "4.22.1",
"version": "4.23.0",
"description": "Mailgun provider for strapi email plugin",
"keywords": [
"email",
@ -44,14 +44,14 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"form-data": "^4.0.0",
"mailgun.js": "8.2.1"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-email-nodemailer",
"version": "4.22.1",
"version": "4.23.0",
"description": "Nodemailer provider for Strapi 3",
"keywords": [
"strapi",
@ -59,10 +59,10 @@
"nodemailer": "6.9.1"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@types/nodemailer": "6.4.7",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-email-sendgrid",
"version": "4.22.1",
"version": "4.23.0",
"description": "Sendgrid provider for strapi email",
"keywords": [
"email",
@ -43,12 +43,12 @@
},
"dependencies": {
"@sendgrid/mail": "7.7.0",
"@strapi/utils": "4.22.1"
"@strapi/utils": "4.23.0"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-email-sendmail",
"version": "4.22.1",
"version": "4.23.0",
"description": "Sendmail provider for strapi email",
"keywords": [
"email",
@ -41,14 +41,14 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"sendmail": "^1.6.1"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@types/sendmail": "1.4.4",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-upload-aws-s3",
"version": "4.22.1",
"version": "4.23.0",
"description": "AWS S3 provider for strapi upload",
"keywords": [
"upload",
@ -52,10 +52,10 @@
"lodash": "4.17.21"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@types/jest": "29.5.2",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-upload-cloudinary",
"version": "4.22.1",
"version": "4.23.0",
"description": "Cloudinary provider for strapi upload",
"keywords": [
"upload",
@ -42,14 +42,14 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"cloudinary": "^1.41.0",
"into-stream": "^5.1.0"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/provider-upload-local",
"version": "4.22.1",
"version": "4.23.0",
"description": "Local provider for strapi upload",
"keywords": [
"upload",
@ -43,14 +43,14 @@
"watch": "pack-up watch"
},
"dependencies": {
"@strapi/utils": "4.22.1",
"@strapi/utils": "4.23.0",
"fs-extra": "10.0.0"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@types/jest": "29.5.2",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "api-tests",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"dependencies": {
"dotenv": "14.2.0",

View File

@ -1,6 +1,6 @@
{
"name": "eslint-config-custom",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"main": "index.js"
}

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/logger",
"version": "4.22.1",
"version": "4.23.0",
"description": "Strapi's logger",
"homepage": "https://strapi.io",
"bugs": {
@ -42,9 +42,9 @@
"winston": "3.10.0"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"eslint-config-custom": "4.22.1",
"tsconfig": "4.22.1"
"@strapi/pack-up": "4.23.0",
"eslint-config-custom": "4.23.0",
"tsconfig": "4.23.0"
},
"engines": {
"node": ">=18.0.0 <=20.x.x",

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/pack-up",
"version": "4.22.1",
"version": "4.23.0",
"description": "Simple tools for creating interoperable CJS & ESM packages.",
"keywords": [
"strapi",
@ -83,11 +83,11 @@
"yup": "0.32.9"
},
"devDependencies": {
"@strapi/pack-up": "4.22.1",
"@strapi/pack-up": "4.23.0",
"@types/git-url-parse": "9.0.1",
"@types/ini": "1.3.31",
"@types/prompts": "2.4.4",
"eslint-config-custom": "4.22.1",
"eslint-config-custom": "4.23.0",
"rimraf": "3.0.2"
},
"engines": {

View File

@ -1,6 +1,6 @@
{
"name": "tsconfig",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"devDependencies": {
"@tsconfig/node18": "18.2.2"

View File

@ -1,6 +1,6 @@
{
"name": "@strapi/typescript-utils",
"version": "4.22.1",
"version": "4.23.0",
"description": "Typescript support for Strapi",
"keywords": [
"strapi",

View File

@ -1,6 +1,6 @@
{
"name": "scripts-front",
"version": "4.22.1",
"version": "4.23.0",
"private": true,
"scripts": {
"test:front": "jest --config jest.config.front.js"

View File

@ -93,33 +93,54 @@ describeOnCondition(edition === 'EE')('Releases page', () => {
});
test('A user should be able to perform bulk release on entries', async ({ page }) => {
await page.getByRole('link', { name: 'Content Manager' }).click();
await test.step('bulk release', async () => {
// Navigate to the releases page
await page.getByRole('link', { name: 'Releases' }).click();
await page.getByRole('button', { name: 'New release' }).click();
await expect(page.getByRole('dialog', { name: 'New release' })).toBeVisible();
// Create a new release
const newReleaseName = 'The Diamond Dogs';
await page.getByRole('textbox', { name: 'Name' }).fill(newReleaseName);
// Uncheck default scheduling of a release and save
await page.getByRole('checkbox', { name: 'Schedule release' }).uncheck();
await page.getByRole('button', { name: 'Continue' }).click();
// Wait for client side redirect to created release
await page.waitForURL('/admin/plugins/content-releases/*');
await expect(page.getByRole('heading', { name: newReleaseName })).toBeVisible();
await expect(page).toHaveTitle('Content Manager');
await expect(page.getByRole('heading', { name: 'Article' })).toBeVisible();
const publishedItems = page.getByRole('gridcell', { name: 'published' });
expect(publishedItems).toHaveCount(2);
const checkbox = page.getByRole('checkbox', { name: 'Select all entries' });
// Navigate to the content manager
await page.getByRole('link', { name: 'Open the Content Manager' }).click();
await expect(page).toHaveTitle('Content Manager');
await expect(page.getByRole('heading', { name: 'Article' })).toBeVisible();
expect(page.getByRole('gridcell', { name: 'published' })).toHaveCount(2);
// Select all entries to release
await checkbox.check();
const addToRelease = page.getByRole('button', { name: 'add to release' });
await addToRelease.click();
// Select all entries to release
await page.getByRole('checkbox', { name: 'Select all entries' }).check();
await page.getByRole('button', { name: 'add to release' }).click();
// Wait for the add to release dialog to appear
await page
.getByRole('combobox', {
name: 'Select a release',
})
.click();
// Wait for the add to release dialog to appear
await page
.getByRole('combobox', {
name: 'Select a release',
})
.click();
await page.getByRole('option', { name: 'Trent Crimm: The Independent' }).click();
const unpublishButton = page.getByText('unpublish', { exact: true });
await unpublishButton.click();
await page.getByText('continue').click();
await page.getByText(/Successfully added to release./).waitFor({
state: 'visible',
timeout: 5000,
await page.getByRole('option', { name: 'The Diamond Dogs' }).click();
await page.getByText('unpublish', { exact: true }).click();
await page.getByText('continue').click();
await page.getByText(/Successfully added to release./).waitFor({
state: 'visible',
timeout: 5000,
});
});
await test.step('releases should be updated in the release column of list view', async () => {
const releaseColumn = page.getByRole('button', { name: '2 releases' });
expect(releaseColumn).toHaveCount(2);
await releaseColumn.first().click();
await expect(page.getByText('The Diamond Dogs')).toBeVisible();
await expect(page.getByText('Trent Crimm: The Independent')).toBeVisible();
});
});
});

809
yarn.lock

File diff suppressed because it is too large Load Diff