mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 03:43:34 +00:00 
			
		
		
		
	[Bugfix] Bulk publish/unpublish for non-default locale entities (#17941)
* pass the locale to the publish and draft relations calls * fix the problem for entities without internationalization and add unit tests
This commit is contained in:
		
							parent
							
								
									c03a0a4a28
								
							
						
					
					
						commit
						e3a5416487
					
				@ -6,6 +6,7 @@ import {
 | 
				
			|||||||
  useFetchClient,
 | 
					  useFetchClient,
 | 
				
			||||||
  useNotification,
 | 
					  useNotification,
 | 
				
			||||||
  useAPIErrorHandler,
 | 
					  useAPIErrorHandler,
 | 
				
			||||||
 | 
					  useQueryParams,
 | 
				
			||||||
} from '@strapi/helper-plugin';
 | 
					} from '@strapi/helper-plugin';
 | 
				
			||||||
import { Check, ExclamationMarkCircle } from '@strapi/icons';
 | 
					import { Check, ExclamationMarkCircle } from '@strapi/icons';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
@ -82,6 +83,7 @@ const ConfirmDialogPublishAll = ({ isOpen, onToggleDialog, isConfirmButtonLoadin
 | 
				
			|||||||
  const {
 | 
					  const {
 | 
				
			||||||
    contentType: { uid: slug },
 | 
					    contentType: { uid: slug },
 | 
				
			||||||
  } = useSelector(listViewDomain());
 | 
					  } = useSelector(listViewDomain());
 | 
				
			||||||
 | 
					  const [{ query }] = useQueryParams();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const {
 | 
					  const {
 | 
				
			||||||
    data: countDraftRelations,
 | 
					    data: countDraftRelations,
 | 
				
			||||||
@ -97,6 +99,7 @@ const ConfirmDialogPublishAll = ({ isOpen, onToggleDialog, isConfirmButtonLoadin
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
          params: {
 | 
					          params: {
 | 
				
			||||||
            ids: selectedEntries,
 | 
					            ids: selectedEntries,
 | 
				
			||||||
 | 
					            locale: query?.plugins?.i18n?.locale,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { lightTheme, ThemeProvider } from '@strapi/design-system';
 | 
					import { lightTheme, ThemeProvider } from '@strapi/design-system';
 | 
				
			||||||
import { Table } from '@strapi/helper-plugin';
 | 
					import { Table, useQueryParams } from '@strapi/helper-plugin';
 | 
				
			||||||
import { render as renderRTL, screen, waitFor, within } from '@testing-library/react';
 | 
					import { render as renderRTL, screen, waitFor, within } from '@testing-library/react';
 | 
				
			||||||
import { rest } from 'msw';
 | 
					import { rest } from 'msw';
 | 
				
			||||||
import { setupServer } from 'msw/node';
 | 
					import { setupServer } from 'msw/node';
 | 
				
			||||||
@ -34,6 +34,17 @@ jest.mock('@strapi/helper-plugin', () => ({
 | 
				
			|||||||
  useNotification: jest.fn(() => {
 | 
					  useNotification: jest.fn(() => {
 | 
				
			||||||
    return toggleNotification;
 | 
					    return toggleNotification;
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
 | 
					  useQueryParams: jest.fn(() => [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      query: {
 | 
				
			||||||
 | 
					        plugins: {
 | 
				
			||||||
 | 
					          i18n: {
 | 
				
			||||||
 | 
					            locale: 'en',
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ]),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const handlers = [
 | 
					const handlers = [
 | 
				
			||||||
@ -215,4 +226,35 @@ describe('ConfirmDialogPublishAll', () => {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
    expect(await screen.getByRole('alert')).toBeInTheDocument();
 | 
					    expect(await screen.getByRole('alert')).toBeInTheDocument();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should show the warning message with 2 draft relations and 2 entries even if the locale param is not passed', async () => {
 | 
				
			||||||
 | 
					    useQueryParams.mockImplementation(() => [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        query: {
 | 
				
			||||||
 | 
					          page: 1,
 | 
				
			||||||
 | 
					          pageSize: 10,
 | 
				
			||||||
 | 
					          sort: 'name:ASC',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    server.use(
 | 
				
			||||||
 | 
					      rest.get('*/countManyEntriesDraftRelations', (req, res, ctx) => {
 | 
				
			||||||
 | 
					        return res.once(
 | 
				
			||||||
 | 
					          ctx.status(200),
 | 
				
			||||||
 | 
					          ctx.json({
 | 
				
			||||||
 | 
					            data: 2,
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await waitFor(() => {
 | 
				
			||||||
 | 
					      const publishDialog = screen.getByRole('dialog');
 | 
				
			||||||
 | 
					      expect(publishDialog).toBeInTheDocument();
 | 
				
			||||||
 | 
					      within(publishDialog).getByText(/2 relations out of 2 entries are/i);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -445,7 +445,7 @@ const SelectedEntriesModal = ({ onToggle }) => {
 | 
				
			|||||||
  // We want to keep the selected entries order same as the list view
 | 
					  // We want to keep the selected entries order same as the list view
 | 
				
			||||||
  const [
 | 
					  const [
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      query: { sort },
 | 
					      query: { sort, plugins },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ] = useQueryParams();
 | 
					  ] = useQueryParams();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -458,6 +458,7 @@ const SelectedEntriesModal = ({ onToggle }) => {
 | 
				
			|||||||
        $in: entriesToFetch,
 | 
					        $in: entriesToFetch,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    locale: plugins?.i18n?.locale,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { get } = useFetchClient();
 | 
					  const { get } = useFetchClient();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { lightTheme, ThemeProvider } from '@strapi/design-system';
 | 
					import { lightTheme, ThemeProvider } from '@strapi/design-system';
 | 
				
			||||||
import { Table } from '@strapi/helper-plugin';
 | 
					import { Table, useQueryParams } from '@strapi/helper-plugin';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  render as renderRTL,
 | 
					  render as renderRTL,
 | 
				
			||||||
  screen,
 | 
					  screen,
 | 
				
			||||||
@ -31,6 +31,11 @@ jest.mock('@strapi/helper-plugin', () => ({
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
      query: {
 | 
					      query: {
 | 
				
			||||||
        sort: 'name:DESC',
 | 
					        sort: 'name:DESC',
 | 
				
			||||||
 | 
					        plugins: {
 | 
				
			||||||
 | 
					          i18n: {
 | 
				
			||||||
 | 
					            locale: 'en',
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ]),
 | 
					  ]),
 | 
				
			||||||
@ -159,6 +164,32 @@ describe('Bulk publish selected entries modal', () => {
 | 
				
			|||||||
    expect(screen.queryByText('Entry 4')).not.toBeInTheDocument();
 | 
					    expect(screen.queryByText('Entry 4')).not.toBeInTheDocument();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('renders the selected items in the modal even if the locale param is not passed', async () => {
 | 
				
			||||||
 | 
					    useQueryParams.mockImplementation(() => [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        query: {
 | 
				
			||||||
 | 
					          page: 1,
 | 
				
			||||||
 | 
					          pageSize: 10,
 | 
				
			||||||
 | 
					          sort: 'name:DESC',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { queryByText } = render(
 | 
				
			||||||
 | 
					      <Table.Root defaultSelectedEntries={[1, 2, 3]} colCount={4}>
 | 
				
			||||||
 | 
					        <SelectedEntriesModal onToggle={jest.fn()} />
 | 
				
			||||||
 | 
					      </Table.Root>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await waitForElementToBeRemoved(() => queryByText('Loading content'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(screen.getByText(/publish entries/i)).toBeInTheDocument();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Nested table should render the selected items from the parent table
 | 
				
			||||||
 | 
					    expect(screen.queryByText('Entry 1')).toBeInTheDocument();
 | 
				
			||||||
 | 
					    expect(screen.queryByText('Entry 4')).not.toBeInTheDocument();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('reacts to selection updates', async () => {
 | 
					  it('reacts to selection updates', async () => {
 | 
				
			||||||
    const { queryByText } = render(
 | 
					    const { queryByText } = render(
 | 
				
			||||||
      <Table.Root defaultSelectedEntries={[1, 2, 3]} colCount={4}>
 | 
					      <Table.Root defaultSelectedEntries={[1, 2, 3]} colCount={4}>
 | 
				
			||||||
 | 
				
			|||||||
@ -445,6 +445,7 @@ module.exports = {
 | 
				
			|||||||
  async countManyEntriesDraftRelations(ctx) {
 | 
					  async countManyEntriesDraftRelations(ctx) {
 | 
				
			||||||
    const { userAbility } = ctx.state;
 | 
					    const { userAbility } = ctx.state;
 | 
				
			||||||
    const ids = ctx.request.query.ids;
 | 
					    const ids = ctx.request.query.ids;
 | 
				
			||||||
 | 
					    const locale = ctx.request.query.locale;
 | 
				
			||||||
    const { model } = ctx.params;
 | 
					    const { model } = ctx.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const entityManager = getService('entity-manager');
 | 
					    const entityManager = getService('entity-manager');
 | 
				
			||||||
@ -454,13 +455,13 @@ module.exports = {
 | 
				
			|||||||
      return ctx.forbidden();
 | 
					      return ctx.forbidden();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const entities = await entityManager.find(ids, model);
 | 
					    const entities = await entityManager.find({ ids, locale }, model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!entities) {
 | 
					    if (!entities) {
 | 
				
			||||||
      return ctx.notFound();
 | 
					      return ctx.notFound();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const number = await entityManager.countManyEntriesDraftRelations(ids, model);
 | 
					    const number = await entityManager.countManyEntriesDraftRelations(ids, model, locale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      data: number,
 | 
					      data: number,
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ interface EntityManager {
 | 
				
			|||||||
  publish(): any;
 | 
					  publish(): any;
 | 
				
			||||||
  unpublish(): any;
 | 
					  unpublish(): any;
 | 
				
			||||||
  countDraftRelations(id: string, uid: string): number;
 | 
					  countDraftRelations(id: string, uid: string): number;
 | 
				
			||||||
  countManyEntriesDraftRelations(ids: number[], uid: string): number;
 | 
					  countManyEntriesDraftRelations(ids: number[], uid: string, locale?: string): number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function (opts: { strapi: Strapi }): EntityManager;
 | 
					export default function (opts: { strapi: Strapi }): EntityManager;
 | 
				
			||||||
 | 
				
			|||||||
@ -301,7 +301,7 @@ module.exports = ({ strapi }) => ({
 | 
				
			|||||||
    return sumDraftCounts(entity, uid);
 | 
					    return sumDraftCounts(entity, uid);
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async countManyEntriesDraftRelations(ids, uid) {
 | 
					  async countManyEntriesDraftRelations(ids, uid, locale = 'en') {
 | 
				
			||||||
    const { populate, hasRelations } = getDeepPopulateDraftCount(uid);
 | 
					    const { populate, hasRelations } = getDeepPopulateDraftCount(uid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!hasRelations) {
 | 
					    if (!hasRelations) {
 | 
				
			||||||
@ -311,6 +311,7 @@ module.exports = ({ strapi }) => ({
 | 
				
			|||||||
    const entities = await strapi.entityService.findMany(uid, {
 | 
					    const entities = await strapi.entityService.findMany(uid, {
 | 
				
			||||||
      populate,
 | 
					      populate,
 | 
				
			||||||
      filters: { id: { $in: ids } },
 | 
					      filters: { id: { $in: ids } },
 | 
				
			||||||
 | 
					      locale,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const totalNumberDraftRelations = entities.reduce(
 | 
					    const totalNumberDraftRelations = entities.reduce(
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user