mirror of
https://github.com/strapi/strapi.git
synced 2025-11-01 02:16:03 +00:00
chore(i18n): convert CM List view components to TS (#18838)
This commit is contained in:
parent
306b218011
commit
a1c8cbb8a8
File diff suppressed because one or more lines are too long
@ -1,41 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Typography } from '@strapi/design-system';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import useHasI18n from '../../../hooks/useHasI18n';
|
||||
import { getTranslation } from '../../../utils/getTranslation';
|
||||
|
||||
const Emphasis = (chunks: React.ReactNode) => {
|
||||
return (
|
||||
<Typography fontWeight="semiBold" textColor="danger500">
|
||||
{chunks}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
const DeleteModalAdditionalInfos = () => {
|
||||
const hasI18nEnabled = useHasI18n();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
if (!hasI18nEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography textColor="danger500">
|
||||
{formatMessage(
|
||||
{
|
||||
id: getTranslation('Settings.list.actions.deleteAdditionalInfos'),
|
||||
defaultMessage:
|
||||
'This will delete the active locale versions <em>(from Internationalization)</em>',
|
||||
},
|
||||
{
|
||||
em: Emphasis,
|
||||
}
|
||||
)}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteModalAdditionalInfos;
|
||||
@ -1,41 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Typography } from '@strapi/design-system';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import useHasI18n from '../../../hooks/useHasI18n';
|
||||
import { getTranslation } from '../../../utils/getTranslation';
|
||||
|
||||
const Emphasis = (chunks: React.ReactNode) => {
|
||||
return (
|
||||
<Typography fontWeight="semiBold" textColor="danger500">
|
||||
{chunks}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
const PublishModalAdditionalInfos = () => {
|
||||
const hasI18nEnabled = useHasI18n();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
if (!hasI18nEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography textColor="danger500">
|
||||
{formatMessage(
|
||||
{
|
||||
id: getTranslation('Settings.list.actions.publishAdditionalInfos'),
|
||||
defaultMessage:
|
||||
'This will publish the active locale versions <em>(from Internationalization)</em>',
|
||||
},
|
||||
{
|
||||
em: Emphasis,
|
||||
}
|
||||
)}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
export default PublishModalAdditionalInfos;
|
||||
@ -1,41 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Typography } from '@strapi/design-system';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import useHasI18n from '../../../hooks/useHasI18n';
|
||||
import { getTranslation } from '../../../utils/getTranslation';
|
||||
|
||||
const Emphasis = (chunks: React.ReactNode) => {
|
||||
return (
|
||||
<Typography fontWeight="semiBold" textColor="danger500">
|
||||
{chunks}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
const UnpublishModalAdditionalInfos = () => {
|
||||
const hasI18nEnabled = useHasI18n();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
if (!hasI18nEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography textColor="danger500">
|
||||
{formatMessage(
|
||||
{
|
||||
id: getTranslation('Settings.list.actions.unpublishAdditionalInfos'),
|
||||
defaultMessage:
|
||||
'This will unpublish the active locale versions <em>(from Internationalization)</em>',
|
||||
},
|
||||
{
|
||||
em: Emphasis,
|
||||
}
|
||||
)}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnpublishModalAdditionalInfos;
|
||||
@ -0,0 +1,89 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Typography } from '@strapi/design-system';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { useContentTypeHasI18n } from '../hooks/useContentTypeHasI18n';
|
||||
import { getTranslation } from '../utils/getTranslation';
|
||||
|
||||
const Emphasis = (chunks: React.ReactNode) => {
|
||||
return (
|
||||
<Typography fontWeight="semiBold" textColor="danger500">
|
||||
{chunks}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
const DeleteModalAdditionalInfo = () => {
|
||||
const hasI18nEnabled = useContentTypeHasI18n();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
if (!hasI18nEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography textColor="danger500">
|
||||
{formatMessage(
|
||||
{
|
||||
id: getTranslation('Settings.list.actions.deleteAdditionalInfos'),
|
||||
defaultMessage:
|
||||
'This will delete the active locale versions <em>(from Internationalization)</em>',
|
||||
},
|
||||
{
|
||||
em: Emphasis,
|
||||
}
|
||||
)}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
const PublishModalAdditionalInfo = () => {
|
||||
const hasI18nEnabled = useContentTypeHasI18n();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
if (!hasI18nEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography textColor="danger500">
|
||||
{formatMessage(
|
||||
{
|
||||
id: getTranslation('Settings.list.actions.publishAdditionalInfos'),
|
||||
defaultMessage:
|
||||
'This will publish the active locale versions <em>(from Internationalization)</em>',
|
||||
},
|
||||
{
|
||||
em: Emphasis,
|
||||
}
|
||||
)}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
const UnpublishModalAdditionalInfo = () => {
|
||||
const hasI18nEnabled = useContentTypeHasI18n();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
if (!hasI18nEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Typography textColor="danger500">
|
||||
{formatMessage(
|
||||
{
|
||||
id: getTranslation('Settings.list.actions.unpublishAdditionalInfos'),
|
||||
defaultMessage:
|
||||
'This will unpublish the active locale versions <em>(from Internationalization)</em>',
|
||||
},
|
||||
{
|
||||
em: Emphasis,
|
||||
}
|
||||
)}
|
||||
</Typography>
|
||||
);
|
||||
};
|
||||
|
||||
export { DeleteModalAdditionalInfo, PublishModalAdditionalInfo, UnpublishModalAdditionalInfo };
|
||||
84
packages/plugins/i18n/admin/src/components/LocalePicker.tsx
Normal file
84
packages/plugins/i18n/admin/src/components/LocalePicker.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { SingleSelect, SingleSelectOption, SingleSelectProps } from '@strapi/design-system';
|
||||
import { useQueryParams } from '@strapi/helper-plugin';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useRouteMatch } from 'react-router-dom';
|
||||
|
||||
import { useContentTypeHasI18n } from '../hooks/useContentTypeHasI18n';
|
||||
import { useContentTypePermissions } from '../hooks/useContentTypePermissions';
|
||||
import { useTypedSelector } from '../store/hooks';
|
||||
import { getTranslation } from '../utils/getTranslation';
|
||||
import { getInitialLocale } from '../utils/locales';
|
||||
|
||||
const LocalePicker = () => {
|
||||
const { formatMessage } = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const locales = useTypedSelector((state) => state.i18n_locales.locales);
|
||||
const [{ query }, setQuery] = useQueryParams<{
|
||||
page: number;
|
||||
plugins: { i18n: { locale: string } };
|
||||
}>();
|
||||
const match = useRouteMatch<{ slug: string }>('/content-manager/collectionType/:slug');
|
||||
const isContentTypeLocalized = useContentTypeHasI18n();
|
||||
const { createPermissions, readPermissions } = useContentTypePermissions(match?.params.slug);
|
||||
|
||||
const initialLocale = getInitialLocale(query, locales);
|
||||
const [selected, setSelected] = useState(initialLocale?.code || '');
|
||||
|
||||
if (!isContentTypeLocalized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!locales || locales.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayedLocales = locales.filter((locale) => {
|
||||
const canCreate = createPermissions.some(({ properties }) =>
|
||||
(properties?.locales ?? []).includes(locale.code)
|
||||
);
|
||||
const canRead = readPermissions.some(({ properties }) =>
|
||||
(properties?.locales ?? []).includes(locale.code)
|
||||
);
|
||||
|
||||
return canCreate || canRead;
|
||||
});
|
||||
|
||||
// @ts-expect-error – This can be removed in V2 of the DS.
|
||||
const handleChange: SingleSelectProps['onChange'] = (code: string) => {
|
||||
if (code === selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSelected(code);
|
||||
|
||||
dispatch({ type: 'ContentManager/RBACManager/RESET_PERMISSIONS' });
|
||||
|
||||
setQuery({
|
||||
page: 1,
|
||||
plugins: { ...query.plugins, i18n: { locale: code } },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<SingleSelect
|
||||
size="S"
|
||||
aria-label={formatMessage({
|
||||
id: getTranslation('actions.select-locale'),
|
||||
defaultMessage: 'Select locale',
|
||||
})}
|
||||
value={selected}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{displayedLocales.map((locale) => (
|
||||
<SingleSelectOption key={locale.id} value={locale.code}>
|
||||
{locale.name}
|
||||
</SingleSelectOption>
|
||||
))}
|
||||
</SingleSelect>
|
||||
);
|
||||
};
|
||||
|
||||
export { LocalePicker };
|
||||
@ -1,93 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Option, Select } from '@strapi/design-system';
|
||||
import { useQueryParams } from '@strapi/helper-plugin';
|
||||
import get from 'lodash/get';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useRouteMatch } from 'react-router-dom';
|
||||
|
||||
import { useContentTypePermissions } from '../../hooks/useContentTypePermissions';
|
||||
import useHasI18n from '../../hooks/useHasI18n';
|
||||
import { useTypedSelector } from '../../store/hooks';
|
||||
import getInitialLocale from '../../utils/getInitialLocale';
|
||||
import { getTranslation } from '../../utils/getTranslation';
|
||||
|
||||
const LocalePicker = () => {
|
||||
const { formatMessage } = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const locales = useTypedSelector((state) => state.i18n_locales.locales);
|
||||
const [{ query }, setQuery] = useQueryParams<any>();
|
||||
const {
|
||||
params: { slug },
|
||||
} = useRouteMatch('/content-manager/collectionType/:slug') as any;
|
||||
const isFieldLocalized = useHasI18n();
|
||||
const { createPermissions, readPermissions } = useContentTypePermissions(slug);
|
||||
|
||||
const initialLocale = getInitialLocale(query, locales);
|
||||
const [selected, setSelected] = useState(initialLocale?.code || '');
|
||||
|
||||
if (!isFieldLocalized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!locales || locales.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayedLocales = locales.filter((locale: any) => {
|
||||
const canCreate = createPermissions.find(({ properties }: any) => {
|
||||
return get(properties, 'locales', []).includes(locale.code);
|
||||
});
|
||||
const canRead = readPermissions.find(({ properties }: any) =>
|
||||
get(properties, 'locales', []).includes(locale.code)
|
||||
);
|
||||
|
||||
return canCreate || canRead;
|
||||
});
|
||||
|
||||
const handleClick = (code: string) => {
|
||||
if (code === selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSelected(code);
|
||||
|
||||
/**
|
||||
* if the selected value is set at the same time as the dispatcher
|
||||
* is run, react might not have enough time to re-render the Select
|
||||
* component, which leads to the `source` ref, which is passed to
|
||||
* Popout, not being defined.
|
||||
*
|
||||
* By pushing the dispatcher to the end of the current execution
|
||||
* context, we can guarantee the rendering can finish before.
|
||||
*/
|
||||
setTimeout(() => {
|
||||
dispatch({ type: 'ContentManager/RBACManager/RESET_PERMISSIONS' });
|
||||
setQuery({
|
||||
page: 1,
|
||||
plugins: { ...query.plugins, i18n: { locale: code } },
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
size="S"
|
||||
aria-label={formatMessage({
|
||||
id: getTranslation('actions.select-locale'),
|
||||
defaultMessage: '',
|
||||
})}
|
||||
value={selected}
|
||||
onChange={handleClick as any}
|
||||
>
|
||||
{displayedLocales.map((locale) => (
|
||||
<Option key={locale.id} id={`menu-item${locale.name || locale.code}`} value={locale.code}>
|
||||
{locale.name}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocalePicker;
|
||||
@ -1,7 +1,7 @@
|
||||
import get from 'lodash/get';
|
||||
import { parse, stringify } from 'qs';
|
||||
|
||||
import getDefaultLocale from '../../utils/getDefaultLocale';
|
||||
import { getDefaultLocale } from '../../utils/locales';
|
||||
|
||||
const addLocaleToLinksSearch = (
|
||||
links: any[],
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const useContentTypeHasI18n = jest.fn().mockReturnValue(true);
|
||||
@ -0,0 +1,12 @@
|
||||
import { useTypedSelector } from '../store/hooks';
|
||||
|
||||
const useContentTypeHasI18n = (): boolean => {
|
||||
const pluginOptions = useTypedSelector(
|
||||
// @ts-expect-error – we've not typed the CM ListView yet.
|
||||
(state) => state['content-manager_listView'].contentType.pluginOptions
|
||||
);
|
||||
|
||||
return pluginOptions?.i18n?.localized ?? false;
|
||||
};
|
||||
|
||||
export { useContentTypeHasI18n };
|
||||
@ -1,24 +1,37 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { Permission } from '@strapi/helper-plugin';
|
||||
|
||||
import { useTypedSelector } from '../store/hooks';
|
||||
import { RootState } from '../store/reducers';
|
||||
|
||||
const selectContentTypePermissions = createSelector(
|
||||
(state: RootState) => state.rbacProvider.collectionTypesRelatedPermissions,
|
||||
(_, slug: string) => slug,
|
||||
(state, slug) => {
|
||||
// @ts-expect-error – Selectors are weird, why don't they work with TS?
|
||||
const currentCTRelatedPermissions = state[slug];
|
||||
const readPermissions =
|
||||
currentCTRelatedPermissions['plugin::content-manager.explorer.read'] || [];
|
||||
const createPermissions =
|
||||
currentCTRelatedPermissions['plugin::content-manager.explorer.create'] || [];
|
||||
const makeSelectContentTypePermissions = () =>
|
||||
// @ts-expect-error – I have no idea why this fails like this.
|
||||
createSelector(
|
||||
(state: RootState) => state.rbacProvider.collectionTypesRelatedPermissions,
|
||||
(_, slug: string) => slug,
|
||||
(state: RootState['rbacProvider']['collectionTypesRelatedPermissions'], slug: string) => {
|
||||
const currentCTRelatedPermissions = slug ? state[slug] : {};
|
||||
|
||||
return { createPermissions, readPermissions };
|
||||
}
|
||||
);
|
||||
if (!currentCTRelatedPermissions) {
|
||||
return { createPermissions: [], readPermissions: [] };
|
||||
}
|
||||
|
||||
const useContentTypePermissions = (slug: string) =>
|
||||
useTypedSelector((state) => selectContentTypePermissions(state, slug));
|
||||
const readPermissions =
|
||||
currentCTRelatedPermissions['plugin::content-manager.explorer.read'] || [];
|
||||
const createPermissions =
|
||||
currentCTRelatedPermissions['plugin::content-manager.explorer.create'] || [];
|
||||
|
||||
return { createPermissions, readPermissions };
|
||||
}
|
||||
);
|
||||
|
||||
const useContentTypePermissions = (
|
||||
slug?: string
|
||||
): { createPermissions: Permission[]; readPermissions: Permission[] } => {
|
||||
const selectContentTypePermissions = useMemo(makeSelectContentTypePermissions, []);
|
||||
return useTypedSelector((state) => selectContentTypePermissions(state, slug));
|
||||
};
|
||||
|
||||
export { useContentTypePermissions };
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
import get from 'lodash/get';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
const selectContentManagerListViewPluginOptions = (state: any) =>
|
||||
state['content-manager_listView'].contentType.pluginOptions;
|
||||
|
||||
const useHasI18n = () => {
|
||||
const pluginOptions = useSelector(selectContentManagerListViewPluginOptions);
|
||||
|
||||
return get(pluginOptions, 'i18n.localized', false);
|
||||
};
|
||||
|
||||
export default useHasI18n;
|
||||
@ -4,11 +4,13 @@ import * as yup from 'yup';
|
||||
|
||||
import CheckboxConfirmation from './components/CheckboxConfirmation';
|
||||
import { CMEditViewInjectedComponents } from './components/CMEditViewInjectedComponents';
|
||||
import DeleteModalAdditionalInfos from './components/CMListViewInjectedComponents/DeleteModalAdditionalInfos';
|
||||
import PublishModalAdditionalInfos from './components/CMListViewInjectedComponents/PublishModalAdditionalInfos';
|
||||
import UnpublishModalAdditionalInfos from './components/CMListViewInjectedComponents/UnpublishModalAdditionalInfos';
|
||||
import {
|
||||
DeleteModalAdditionalInfo,
|
||||
PublishModalAdditionalInfo,
|
||||
UnpublishModalAdditionalInfo,
|
||||
} from './components/CMListViewModalsAdditionalInformation';
|
||||
import Initializer from './components/Initializer';
|
||||
import LocalePicker from './components/LocalePicker';
|
||||
import { LocalePicker } from './components/LocalePicker';
|
||||
import { PERMISSIONS } from './constants';
|
||||
import addColumnToTableHook from './contentManagerHooks/addColumnToTable';
|
||||
import addLocaleToCollectionTypesLinksHook from './contentManagerHooks/addLocaleToCollectionTypesLinks';
|
||||
@ -81,17 +83,17 @@ export default {
|
||||
|
||||
app.injectContentManagerComponent('listView', 'deleteModalAdditionalInfos', {
|
||||
name: 'i18n-delete-bullets-in-modal',
|
||||
Component: DeleteModalAdditionalInfos,
|
||||
Component: DeleteModalAdditionalInfo,
|
||||
});
|
||||
|
||||
app.injectContentManagerComponent('listView', 'publishModalAdditionalInfos', {
|
||||
name: 'i18n-publish-bullets-in-modal',
|
||||
Component: PublishModalAdditionalInfos,
|
||||
Component: PublishModalAdditionalInfo,
|
||||
});
|
||||
|
||||
app.injectContentManagerComponent('listView', 'unpublishModalAdditionalInfos', {
|
||||
name: 'i18n-unpublish-bullets-in-modal',
|
||||
Component: UnpublishModalAdditionalInfos,
|
||||
Component: UnpublishModalAdditionalInfo,
|
||||
});
|
||||
|
||||
const ctbPlugin = app.getPlugin('content-type-builder');
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createSelector, Dispatch, Selector } from '@reduxjs/toolkit';
|
||||
import { Dispatch } from '@reduxjs/toolkit';
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { Action, RootState } from './reducers';
|
||||
@ -8,9 +8,4 @@ type AppDispatch = Dispatch<Action>;
|
||||
const useTypedDispatch: () => AppDispatch = useDispatch;
|
||||
const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||
|
||||
const createTypedSelector = <TResult, TSelector extends Selector<RootState, TResult>>(
|
||||
selector: TSelector
|
||||
// @ts-expect-error – TODO: this is needed to avoid TS2742. But it's not quite right.
|
||||
): ReturnType<TSelector> => createSelector((state: RootState) => state, selector);
|
||||
|
||||
export { useTypedSelector, createTypedSelector, useTypedDispatch };
|
||||
export { useTypedSelector, useTypedDispatch };
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
import get from 'lodash/get';
|
||||
|
||||
const hasLocalePermission = (permissions: any, localeCode: any) => {
|
||||
if (permissions) {
|
||||
const hasPermission = permissions.some((permission: any) =>
|
||||
get(permission, 'properties.locales', []).includes(localeCode)
|
||||
);
|
||||
|
||||
if (hasPermission) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const getFirstLocale = (permissions: any) => {
|
||||
if (permissions && permissions.length > 0) {
|
||||
const firstAuthorizedNonDefaultLocale = get(permissions, [0, 'properties', 'locales', 0], null);
|
||||
|
||||
if (firstAuthorizedNonDefaultLocale) {
|
||||
return firstAuthorizedNonDefaultLocale;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Entry point of the module
|
||||
*/
|
||||
const getDefaultLocale = (ctPermissions: any, locales: any = []) => {
|
||||
const defaultLocale = locales.find((locale: any) => locale.isDefault);
|
||||
|
||||
if (!defaultLocale) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const readPermissions = ctPermissions['plugin::content-manager.explorer.read'];
|
||||
const createPermissions = ctPermissions['plugin::content-manager.explorer.create'];
|
||||
|
||||
if (hasLocalePermission(readPermissions, defaultLocale.code)) {
|
||||
return defaultLocale.code;
|
||||
}
|
||||
|
||||
if (hasLocalePermission(createPermissions, defaultLocale.code)) {
|
||||
return defaultLocale.code;
|
||||
}
|
||||
|
||||
// When the default locale is not authorized, we return the first authorized locale
|
||||
const firstAuthorizedForReadNonDefaultLocale = getFirstLocale(readPermissions);
|
||||
|
||||
if (firstAuthorizedForReadNonDefaultLocale) {
|
||||
return firstAuthorizedForReadNonDefaultLocale;
|
||||
}
|
||||
|
||||
return getFirstLocale(createPermissions);
|
||||
};
|
||||
|
||||
export default getDefaultLocale;
|
||||
@ -1,14 +0,0 @@
|
||||
import getLocaleFromQuery from './getLocaleFromQuery';
|
||||
|
||||
const getInitialLocale = (query: any, locales: any = []) => {
|
||||
const localeFromQuery = getLocaleFromQuery(query);
|
||||
|
||||
if (localeFromQuery) {
|
||||
return locales.find((locale: any) => locale.code === localeFromQuery);
|
||||
}
|
||||
|
||||
// Returns the default locale when nothing is in the query
|
||||
return locales.find((locale: any) => locale.isDefault);
|
||||
};
|
||||
|
||||
export default getInitialLocale;
|
||||
@ -1,7 +0,0 @@
|
||||
import get from 'lodash/get';
|
||||
|
||||
const getLocaleFromQuery = (query: any) => {
|
||||
return get(query, 'plugins.i18n.locale', undefined);
|
||||
};
|
||||
|
||||
export default getLocaleFromQuery;
|
||||
76
packages/plugins/i18n/admin/src/utils/locales.ts
Normal file
76
packages/plugins/i18n/admin/src/utils/locales.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { Locale, RootState } from '../store/reducers';
|
||||
|
||||
interface PotentialQueryWithLocale {
|
||||
plugins?: { i18n?: { locale?: string; [key: string]: unknown }; [key: string]: unknown };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locale from the passed query.
|
||||
* If a default value is passed, it will return it if the locale does not exist.
|
||||
*/
|
||||
function getLocaleFromQuery(query: PotentialQueryWithLocale): string | undefined;
|
||||
function getLocaleFromQuery(query: PotentialQueryWithLocale, defaultValue: string): string;
|
||||
function getLocaleFromQuery(
|
||||
query: PotentialQueryWithLocale,
|
||||
defaultValue?: string
|
||||
): string | undefined {
|
||||
const locale = query?.plugins?.i18n?.locale;
|
||||
|
||||
if (!locale && defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initial locale from the query falling back to the default locale
|
||||
* listed in the collection of locales provided.
|
||||
*/
|
||||
const getInitialLocale = (
|
||||
query: PotentialQueryWithLocale,
|
||||
locales: Locale[] = []
|
||||
): Locale | undefined => {
|
||||
const localeFromQuery = getLocaleFromQuery(query);
|
||||
|
||||
if (localeFromQuery) {
|
||||
return locales.find((locale) => locale.code === localeFromQuery);
|
||||
}
|
||||
|
||||
// Returns the default locale when nothing is in the query
|
||||
return locales.find((locale) => locale.isDefault);
|
||||
};
|
||||
|
||||
const getDefaultLocale = (
|
||||
ctPermissions: RootState['rbacProvider']['collectionTypesRelatedPermissions'][string],
|
||||
locales: Locale[] = []
|
||||
) => {
|
||||
const defaultLocale = locales.find((locale) => locale.isDefault);
|
||||
|
||||
if (!defaultLocale) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const readPermissions = ctPermissions['plugin::content-manager.explorer.read'] ?? [];
|
||||
const createPermissions = ctPermissions['plugin::content-manager.explorer.create'] ?? [];
|
||||
|
||||
if (
|
||||
readPermissions.some(({ properties }) =>
|
||||
(properties?.locales ?? []).includes(defaultLocale.code)
|
||||
) ||
|
||||
createPermissions.some(({ properties }) =>
|
||||
(properties?.locales ?? []).includes(defaultLocale.code)
|
||||
)
|
||||
) {
|
||||
return defaultLocale.code;
|
||||
}
|
||||
|
||||
// When the default locale is not authorized, we return the first authorized locale
|
||||
return (
|
||||
(readPermissions[0]?.properties?.locales?.[0] ||
|
||||
createPermissions[0]?.properties?.locales?.[0]) ??
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
export { getLocaleFromQuery, getInitialLocale, getDefaultLocale };
|
||||
@ -1,337 +0,0 @@
|
||||
import getDefaultLocale from '../getDefaultLocale';
|
||||
|
||||
describe('getDefaultLocale', () => {
|
||||
it('gives fr-FR when it s the default locale and that it has read access to it', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: ['en', 'fr-FR'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = 'fr-FR';
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives fr-FR when it s the default locale and that it has create access to it', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: ['fr-FR'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: ['en'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = 'fr-FR';
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives gives the first locale with read permission ("en") when the locale is allowed', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Another lang',
|
||||
code: 'de',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: ['en', 'de'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = 'en';
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives gives the first locale with create permission ("en") when the locale is allowed', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Another lang',
|
||||
code: 'de',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: ['en', 'de'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = 'en';
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives null when the user has no permission on any locale', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Another lang',
|
||||
code: 'de',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = null;
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
@ -1,106 +0,0 @@
|
||||
import getInitialLocale from '../getInitialLocale';
|
||||
|
||||
describe('getInitialLocale', () => {
|
||||
it('gives "fr-FR" when the query.plugins.locale is "fr-FR"', () => {
|
||||
const query = {
|
||||
page: '1',
|
||||
pageSize: '10',
|
||||
sort: 'Name:ASC',
|
||||
plugins: {
|
||||
i18n: { locale: 'fr-FR' },
|
||||
},
|
||||
};
|
||||
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-09T15:03:06.996Z',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const expected = {
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-09T15:03:06.996Z',
|
||||
isDefault: false,
|
||||
};
|
||||
const actual = getInitialLocale(query, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives the default locale ("en") when there s no locale in the query', () => {
|
||||
const query = {
|
||||
page: '1',
|
||||
pageSize: '10',
|
||||
sort: 'Name:ASC',
|
||||
plugins: {
|
||||
something: 'great',
|
||||
},
|
||||
};
|
||||
|
||||
const locales = [
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-09T15:03:06.996Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: true,
|
||||
},
|
||||
];
|
||||
|
||||
const expected = {
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: true,
|
||||
};
|
||||
|
||||
const actual = getInitialLocale(query, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives "undefined" when theres no locale. IMPORTANT: such case should not exist since at least one locale is created on the backend when plug-in i18n', () => {
|
||||
const query = {
|
||||
page: '1',
|
||||
pageSize: '10',
|
||||
sort: 'Name:ASC',
|
||||
plugins: {
|
||||
something: 'great',
|
||||
},
|
||||
};
|
||||
|
||||
const locales: any = [];
|
||||
|
||||
const expected = undefined;
|
||||
const actual = getInitialLocale(query, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
502
packages/plugins/i18n/admin/src/utils/tests/locales.test.ts
Normal file
502
packages/plugins/i18n/admin/src/utils/tests/locales.test.ts
Normal file
@ -0,0 +1,502 @@
|
||||
import { Locale } from '../../store/reducers';
|
||||
import { getInitialLocale, getLocaleFromQuery, getDefaultLocale } from '../locales';
|
||||
|
||||
describe('locales', () => {
|
||||
describe('getLocaleFromQuery', () => {
|
||||
it('returns the locale from the query', () => {
|
||||
const query = {
|
||||
plugins: {
|
||||
i18n: {
|
||||
locale: 'en-GB',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(getLocaleFromQuery(query)).toBe('en-GB');
|
||||
});
|
||||
|
||||
it("should return undefined if the locale doesn't exist", () => {
|
||||
expect(
|
||||
getLocaleFromQuery({
|
||||
plugins: {
|
||||
i18n: {},
|
||||
},
|
||||
})
|
||||
).toBe(undefined);
|
||||
expect(
|
||||
getLocaleFromQuery({
|
||||
plugins: {},
|
||||
})
|
||||
).toBe(undefined);
|
||||
expect(getLocaleFromQuery({})).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return the default value if the locale does not exist', () => {
|
||||
expect(
|
||||
getLocaleFromQuery(
|
||||
{
|
||||
plugins: {
|
||||
i18n: {},
|
||||
},
|
||||
},
|
||||
'en-GB'
|
||||
)
|
||||
).toBe('en-GB');
|
||||
expect(
|
||||
getLocaleFromQuery(
|
||||
{
|
||||
plugins: {},
|
||||
},
|
||||
'en-GB'
|
||||
)
|
||||
).toBe('en-GB');
|
||||
expect(getLocaleFromQuery({}, 'en-GB')).toBe('en-GB');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInitialLocale', () => {
|
||||
it('gives "fr-FR" when the query.plugins.locale is "fr-FR"', () => {
|
||||
const query = {
|
||||
page: '1',
|
||||
pageSize: '10',
|
||||
sort: 'Name:ASC',
|
||||
plugins: {
|
||||
i18n: { locale: 'en-GB' },
|
||||
},
|
||||
};
|
||||
|
||||
const locales: Locale[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en-GB',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-09T15:03:06.996Z',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const actual = getInitialLocale(query, locales);
|
||||
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
{
|
||||
"code": "en-GB",
|
||||
"createdAt": "2021-03-09T14:57:03.016Z",
|
||||
"id": 1,
|
||||
"isDefault": true,
|
||||
"name": "English",
|
||||
"updatedAt": "2021-03-09T14:57:03.016Z",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('gives the default locale ("en") when there s no locale in the query', () => {
|
||||
const query = {
|
||||
page: '1',
|
||||
pageSize: '10',
|
||||
sort: 'Name:ASC',
|
||||
plugins: {
|
||||
something: 'great',
|
||||
},
|
||||
};
|
||||
|
||||
const locales: Locale[] = [
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-09T15:03:06.996Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en-GB',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: true,
|
||||
},
|
||||
];
|
||||
|
||||
const actual = getInitialLocale(query, locales);
|
||||
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
{
|
||||
"code": "en-GB",
|
||||
"createdAt": "2021-03-09T14:57:03.016Z",
|
||||
"id": 1,
|
||||
"isDefault": true,
|
||||
"name": "English",
|
||||
"updatedAt": "2021-03-09T14:57:03.016Z",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('gives "undefined" when theres no locale', () => {
|
||||
/**
|
||||
* @note this case should not exist since at least one locale
|
||||
* is created on the backend when plug-in i18n. But you can
|
||||
* never trust the server.
|
||||
*/
|
||||
const query = {
|
||||
page: '1',
|
||||
pageSize: '10',
|
||||
sort: 'Name:ASC',
|
||||
plugins: {
|
||||
something: 'great',
|
||||
},
|
||||
};
|
||||
|
||||
const locales: Locale[] = [];
|
||||
|
||||
const actual = getInitialLocale(query, locales);
|
||||
|
||||
expect(actual).toMatchInlineSnapshot(`undefined`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDefaultLocale', () => {
|
||||
it('gives fr-FR when it s the default locale and that it has read access to it', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: ['en', 'fr-FR'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = 'fr-FR';
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives fr-FR when it s the default locale and that it has create access to it', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: ['fr-FR'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: ['en'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = 'fr-FR';
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives gives the first locale with read permission ("en") when the locale is allowed', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Another lang',
|
||||
code: 'de',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: ['en', 'de'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = 'en';
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives gives the first locale with create permission ("en") when the locale is allowed', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Another lang',
|
||||
code: 'de',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: ['en', 'de'],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = 'en';
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('gives null when the user has no permission on any locale', () => {
|
||||
const locales = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
createdAt: '2021-03-09T14:57:03.016Z',
|
||||
updatedAt: '2021-03-09T14:57:03.016Z',
|
||||
isDefault: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'French (France) (fr-FR)',
|
||||
code: 'fr-FR',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Another lang',
|
||||
code: 'de',
|
||||
createdAt: '2021-03-09T15:03:06.992Z',
|
||||
updatedAt: '2021-03-17T13:01:03.569Z',
|
||||
isDefault: false,
|
||||
},
|
||||
];
|
||||
|
||||
const ctPermissions = {
|
||||
'plugin::content-manager.explorer.create': [
|
||||
{
|
||||
id: 1325,
|
||||
action: 'plugin::content-manager.explorer.create',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [
|
||||
'postal_coder',
|
||||
'categories',
|
||||
'cover',
|
||||
'images',
|
||||
'city',
|
||||
'likes',
|
||||
'json',
|
||||
'slug',
|
||||
],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
'plugin::content-manager.explorer.read': [
|
||||
{
|
||||
id: 1326,
|
||||
action: 'plugin::content-manager.explorer.read',
|
||||
subject: 'api::address.address',
|
||||
properties: {
|
||||
fields: [],
|
||||
locales: [],
|
||||
},
|
||||
conditions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const expected = null;
|
||||
const actual = getDefaultLocale(ctPermissions, locales);
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -219,7 +219,7 @@ const getNestedPopulateOfNonLocalizedAttributes = (modelUID: any) => {
|
||||
return attributesToPopulate;
|
||||
};
|
||||
|
||||
export default () => ({
|
||||
const contentTypes = () => ({
|
||||
isLocalizedContentType,
|
||||
getValidLocale,
|
||||
getNewLocalizationsFrom,
|
||||
@ -230,3 +230,8 @@ export default () => ({
|
||||
fillNonLocalizedAttributes,
|
||||
getNestedPopulateOfNonLocalizedAttributes,
|
||||
});
|
||||
|
||||
type ContentTypesService = typeof contentTypes;
|
||||
|
||||
export default contentTypes;
|
||||
export { ContentTypesService };
|
||||
|
||||
@ -277,9 +277,14 @@ const addGraphqlLocalizationAction = (contentType: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
export default () => ({
|
||||
const coreApi = () => ({
|
||||
addCreateLocalizationAction,
|
||||
addGraphqlLocalizationAction,
|
||||
createSanitizer,
|
||||
createCreateLocalizationHandler,
|
||||
});
|
||||
|
||||
type CoreApiService = typeof coreApi;
|
||||
|
||||
export default coreApi;
|
||||
export { CoreApiService };
|
||||
|
||||
@ -196,7 +196,12 @@ const decorator = (service: any) => ({
|
||||
},
|
||||
});
|
||||
|
||||
export default () => ({
|
||||
const entityServiceDecorator = () => ({
|
||||
decorator,
|
||||
wrapParams,
|
||||
});
|
||||
|
||||
type EntityServiceDecoratorService = typeof entityServiceDecorator;
|
||||
|
||||
export default entityServiceDecorator;
|
||||
export type { EntityServiceDecoratorService };
|
||||
|
||||
@ -2,6 +2,11 @@ import { isoLocales } from '../constants';
|
||||
|
||||
const getIsoLocales = () => isoLocales;
|
||||
|
||||
export default () => ({
|
||||
const isoLocalesService = () => ({
|
||||
getIsoLocales,
|
||||
});
|
||||
|
||||
type ISOLocalesService = typeof isoLocalesService;
|
||||
|
||||
export default isoLocalesService;
|
||||
export type { ISOLocalesService };
|
||||
|
||||
@ -79,7 +79,7 @@ const deleteAllLocalizedEntriesFor = async ({ locale }: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
export default () => ({
|
||||
const locales = () => ({
|
||||
find,
|
||||
findById,
|
||||
findByCode,
|
||||
@ -92,3 +92,8 @@ export default () => ({
|
||||
delete: deleteFn,
|
||||
initDefaultLocale,
|
||||
});
|
||||
|
||||
type LocaleService = typeof locales;
|
||||
|
||||
export default locales;
|
||||
export type { LocaleService };
|
||||
|
||||
@ -82,8 +82,13 @@ const syncNonLocalizedAttributes = async (entry: any, { model }: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
export default () => ({
|
||||
const localizations = () => ({
|
||||
assignDefaultLocaleToEntries,
|
||||
syncLocalizations,
|
||||
syncNonLocalizedAttributes,
|
||||
});
|
||||
|
||||
type LocalizationsService = typeof localizations;
|
||||
|
||||
export default localizations;
|
||||
export type { LocalizationsService };
|
||||
|
||||
@ -21,7 +21,12 @@ const sendDidUpdateI18nLocalesEvent = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
export default () => ({
|
||||
const metrics = () => ({
|
||||
sendDidInitializeEvent,
|
||||
sendDidUpdateI18nLocalesEvent,
|
||||
});
|
||||
|
||||
type MetricsService = typeof metrics;
|
||||
|
||||
export default metrics;
|
||||
export type { MetricsService };
|
||||
|
||||
@ -2,8 +2,13 @@ import i18nActionsService from './permissions/actions';
|
||||
import sectionsBuilderService from './permissions/sections-builder';
|
||||
import engineService from './permissions/engine';
|
||||
|
||||
export default () => ({
|
||||
const permissions = () => ({
|
||||
actions: i18nActionsService,
|
||||
sectionsBuilder: sectionsBuilderService,
|
||||
engine: engineService,
|
||||
});
|
||||
|
||||
type PermissionsService = typeof permissions;
|
||||
|
||||
export default permissions;
|
||||
export type { PermissionsService };
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import locales from '../services/locales';
|
||||
import permissions from '../services/permissions';
|
||||
import contentTypes from '../services/content-types';
|
||||
import metrics from '../services/metrics';
|
||||
import entityServiceDecorator from '../services/entity-service-decorator';
|
||||
import coreAPI from '../services/core-api';
|
||||
import ISOLocales from '../services/iso-locales';
|
||||
import localizations from '../services/localizations';
|
||||
import type { LocaleService } from '../services/locales';
|
||||
import type { PermissionsService } from '../services/permissions';
|
||||
import type { ContentTypesService } from '../services/content-types';
|
||||
import type { MetricsService } from '../services/metrics';
|
||||
import type { EntityServiceDecoratorService } from '../services/entity-service-decorator';
|
||||
import type { CoreApiService } from '../services/core-api';
|
||||
import type { ISOLocalesService } from '../services/iso-locales';
|
||||
import type { LocalizationsService } from '../services/localizations';
|
||||
|
||||
type S = {
|
||||
permissions: typeof permissions;
|
||||
metrics: typeof metrics;
|
||||
locales: typeof locales;
|
||||
localizations: typeof localizations;
|
||||
['iso-locales']: typeof ISOLocales;
|
||||
['content-types']: typeof contentTypes;
|
||||
['entity-service-decorator']: typeof entityServiceDecorator;
|
||||
['core-api']: typeof coreAPI;
|
||||
permissions: PermissionsService;
|
||||
metrics: MetricsService;
|
||||
locales: LocaleService;
|
||||
localizations: LocalizationsService;
|
||||
['iso-locales']: ISOLocalesService;
|
||||
['content-types']: ContentTypesService;
|
||||
['entity-service-decorator']: EntityServiceDecoratorService;
|
||||
['core-api']: CoreApiService;
|
||||
};
|
||||
|
||||
const getCoreStore = () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user