@akemona-org/strapi-plugin-i18n 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +19 -0
- package/admin/src/assets/images/logo.svg +1 -0
- package/admin/src/components/CMEditViewCopyLocale/index.js +183 -0
- package/admin/src/components/CMEditViewCopyLocale/utils/cleanData.js +36 -0
- package/admin/src/components/CMEditViewCopyLocale/utils/generateOptions.js +22 -0
- package/admin/src/components/CMEditViewCopyLocale/utils/index.js +2 -0
- package/admin/src/components/CMEditViewCopyLocale/utils/removePasswordAndRelationsFieldFromData.js +54 -0
- package/admin/src/components/CMEditViewCopyLocale/utils/tests/cleanData.test.js +83 -0
- package/admin/src/components/CMEditViewCopyLocale/utils/tests/data.js +219 -0
- package/admin/src/components/CMEditViewCopyLocale/utils/tests/generateOptions.test.js +79 -0
- package/admin/src/components/CMEditViewCopyLocale/utils/tests/removePasswordAndRelationsFieldFromData.test.js +40 -0
- package/admin/src/components/CMEditViewInjectedComponents/index.js +58 -0
- package/admin/src/components/CMEditViewLocalePicker/Option.js +66 -0
- package/admin/src/components/CMEditViewLocalePicker/Wrapper.js +8 -0
- package/admin/src/components/CMEditViewLocalePicker/index.js +160 -0
- package/admin/src/components/CMEditViewLocalePicker/utils/addStatusColorToLocale.js +24 -0
- package/admin/src/components/CMEditViewLocalePicker/utils/createLocalesOption.js +20 -0
- package/admin/src/components/CMEditViewLocalePicker/utils/index.js +2 -0
- package/admin/src/components/CheckboxConfirmation/Wrapper.js +12 -0
- package/admin/src/components/CheckboxConfirmation/index.js +70 -0
- package/admin/src/components/DeleteModalAdditionalInfos/index.js +25 -0
- package/admin/src/components/LocaleList/index.js +101 -0
- package/admin/src/components/LocaleListCell/LocaleListCell.js +90 -0
- package/admin/src/components/LocaleListCell/tests/LocaleListCell.test.js +128 -0
- package/admin/src/components/LocalePicker/index.js +126 -0
- package/admin/src/components/LocaleRow/index.js +77 -0
- package/admin/src/components/ModalCreate/AdvancedForm.js +45 -0
- package/admin/src/components/ModalCreate/BaseForm.js +103 -0
- package/admin/src/components/ModalCreate/index.js +136 -0
- package/admin/src/components/ModalDelete/index.js +49 -0
- package/admin/src/components/ModalEdit/AdvancedForm.js +51 -0
- package/admin/src/components/ModalEdit/BaseForm.js +91 -0
- package/admin/src/components/ModalEdit/index.js +122 -0
- package/admin/src/components/SettingsModal.js +66 -0
- package/admin/src/components/index.js +2 -0
- package/admin/src/containers/Initializer.js +31 -0
- package/admin/src/containers/SettingsPage/LocaleSettingsPage.js +69 -0
- package/admin/src/containers/SettingsPage/index.js +33 -0
- package/admin/src/containers/SettingsPage/tests/SettingsPage.test.js +744 -0
- package/admin/src/containers/SettingsPage/tests/__snapshots__/SettingsPage.test.js.snap +241 -0
- package/admin/src/hooks/constants.js +6 -0
- package/admin/src/hooks/reducers.js +63 -0
- package/admin/src/hooks/tests/reducers.test.js +203 -0
- package/admin/src/hooks/useAddLocale/index.js +60 -0
- package/admin/src/hooks/useContentTypePermissions/index.js +16 -0
- package/admin/src/hooks/useDefaultLocales/index.js +27 -0
- package/admin/src/hooks/useDeleteLocale/index.js +45 -0
- package/admin/src/hooks/useEditLocale/index.js +46 -0
- package/admin/src/hooks/useHasI18n/index.js +13 -0
- package/admin/src/hooks/useLocales/index.js +35 -0
- package/admin/src/index.js +169 -0
- package/admin/src/middlewares/addCommonFieldsToInitialDataMiddleware.js +83 -0
- package/admin/src/middlewares/addLocaleColumnToListViewMiddleware.js +32 -0
- package/admin/src/middlewares/addLocaleToCollectionTypesMiddleware.js +25 -0
- package/admin/src/middlewares/addLocaleToSingleTypesMiddleware.js +25 -0
- package/admin/src/middlewares/extendCMEditViewLayoutMiddleware.js +159 -0
- package/admin/src/middlewares/extendCTBAttributeInitialDataMiddleware.js +58 -0
- package/admin/src/middlewares/extendCTBInitialDataMiddleware.js +33 -0
- package/admin/src/middlewares/index.js +21 -0
- package/admin/src/middlewares/localePermissionMiddleware.js +39 -0
- package/admin/src/middlewares/tests/addCommonFieldsToInitialDataMiddleware.test.js +97 -0
- package/admin/src/middlewares/tests/addLocaleColumnToListViewMiddleware.test.js +68 -0
- package/admin/src/middlewares/tests/addLocaleToCollectionTypesMiddleware.test.js +200 -0
- package/admin/src/middlewares/tests/addLocaleToSingleTypesMiddleware.test.js +193 -0
- package/admin/src/middlewares/tests/extendCMEditViewLayoutMiddleware.test.js +556 -0
- package/admin/src/middlewares/tests/extendCTBAttrributeInitialDataMiddleware.test.js +124 -0
- package/admin/src/middlewares/tests/extendCTBInitialDataMiddleware.test.js +92 -0
- package/admin/src/middlewares/tests/localePermissionMiddleware.test.js +150 -0
- package/admin/src/middlewares/utils/addLocaleToLinksSearch.js +56 -0
- package/admin/src/middlewares/utils/tests/addLocaleToLinksSearch.test.js +137 -0
- package/admin/src/permissions.js +9 -0
- package/admin/src/pluginId.js +5 -0
- package/admin/src/schemas.js +7 -0
- package/admin/src/selectors/selectCollectionTypesRelatedPermissions.js +4 -0
- package/admin/src/selectors/selectI18nLocales.js +3 -0
- package/admin/src/translations/en.json +60 -0
- package/admin/src/translations/fr.json +9 -0
- package/admin/src/translations/index.js +11 -0
- package/admin/src/translations/zh-Hans.json +60 -0
- package/admin/src/utils/getDefaultLocale.js +60 -0
- package/admin/src/utils/getInitialLocale.js +14 -0
- package/admin/src/utils/getLocaleFromQuery.js +7 -0
- package/admin/src/utils/getTrad.js +5 -0
- package/admin/src/utils/index.js +2 -0
- package/admin/src/utils/localizedFields.js +23 -0
- package/admin/src/utils/mutateCTBContentTypeSchema.js +66 -0
- package/admin/src/utils/tests/getDefaultLocale.test.js +337 -0
- package/admin/src/utils/tests/getInitialLocale.test.js +106 -0
- package/admin/src/utils/tests/mutateCTBContentTypeSchema.test.js +205 -0
- package/config/functions/bootstrap.js +57 -0
- package/config/functions/migrations/__tests__/content-type.test.js +255 -0
- package/config/functions/migrations/__tests__/field.test.js +150 -0
- package/config/functions/migrations/content-type/disable/index.js +34 -0
- package/config/functions/migrations/content-type/disable/migrate-for-bookshelf.js +58 -0
- package/config/functions/migrations/content-type/disable/migrate-for-mongoose.js +39 -0
- package/config/functions/migrations/content-type/enable/index.js +40 -0
- package/config/functions/migrations/content-type/utils/index.js +27 -0
- package/config/functions/migrations/field/__tests__/utils.test.js +53 -0
- package/config/functions/migrations/field/index.js +37 -0
- package/config/functions/migrations/field/migrate-for-bookshelf.js +72 -0
- package/config/functions/migrations/field/migrate-for-mongoose.js +24 -0
- package/config/functions/migrations/field/migrate.js +55 -0
- package/config/functions/migrations/field/utils.js +58 -0
- package/config/functions/register.js +46 -0
- package/config/policies/validateLocaleCreation.js +68 -0
- package/config/routes.json +64 -0
- package/constants/__tests__/index.test.js +27 -0
- package/constants/index.js +36 -0
- package/constants/iso-locales.json +2002 -0
- package/controllers/__tests__/content-types.test.js +113 -0
- package/controllers/__tests__/iso-locales.test.js +26 -0
- package/controllers/__tests__/locales.test.js +308 -0
- package/controllers/content-types.js +64 -0
- package/controllers/iso-locales.js +11 -0
- package/controllers/locales.js +104 -0
- package/domain/locale.js +10 -0
- package/middlewares/i18n/defaults.json +5 -0
- package/middlewares/i18n/index.js +28 -0
- package/models/Locale.settings.json +31 -0
- package/oas.yml +195 -0
- package/package.json +31 -0
- package/services/__tests__/__snapshots__/iso-locales.test.js.snap +2006 -0
- package/services/__tests__/content-types.test.js +545 -0
- package/services/__tests__/core-api.test.js +106 -0
- package/services/__tests__/entity-service-decorator.test.js +280 -0
- package/services/__tests__/iso-locales.test.js +11 -0
- package/services/__tests__/locales.test.js +237 -0
- package/services/__tests__/localizations.test.js +187 -0
- package/services/__tests__/metrics.test.js +90 -0
- package/services/content-types.js +200 -0
- package/services/core-api.js +296 -0
- package/services/entity-service-decorator.js +155 -0
- package/services/iso-locales.js +9 -0
- package/services/locales.js +97 -0
- package/services/localizations.js +65 -0
- package/services/metrics.js +24 -0
- package/services/permissions/actions.js +124 -0
- package/services/permissions/engine.js +63 -0
- package/services/permissions/sections-builder.js +48 -0
- package/services/permissions.js +11 -0
- package/tests/content-manager/list-relation.test.e2e.js +122 -0
- package/tests/graphql.test.e2e.js +120 -0
- package/tests/locales.test.e2e.js +414 -0
- package/utils/index.js +20 -0
- package/validation/content-types.js +30 -0
- package/validation/locales.js +39 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import generateOptions from '../generateOptions';
|
|
2
|
+
|
|
3
|
+
const appLocales = [
|
|
4
|
+
{ code: 'en', name: 'English' },
|
|
5
|
+
{ code: 'fr', name: 'French' },
|
|
6
|
+
{ code: 'it', name: 'Italian' },
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
describe('I18n | Components | CMEditViewCopyLocale | utils', () => {
|
|
10
|
+
describe('generateOptions', () => {
|
|
11
|
+
it('should return an array', () => {
|
|
12
|
+
expect(generateOptions([])).toEqual([]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should remove the current locale from the array', () => {
|
|
16
|
+
const permissions = [
|
|
17
|
+
{ properties: { locales: [] } },
|
|
18
|
+
{ properties: { locales: ['en', 'fr', 'it'] } },
|
|
19
|
+
];
|
|
20
|
+
const currentLocale = 'en';
|
|
21
|
+
const localizations = [
|
|
22
|
+
{ published_at: 'test', locale: 'en', id: 1 },
|
|
23
|
+
{ published_at: 'test', locale: 'fr', id: 2 },
|
|
24
|
+
{ published_at: 'test', locale: 'it', id: 3 },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const expected = [
|
|
28
|
+
{ label: 'French', value: 2 },
|
|
29
|
+
{ label: 'Italian', value: 3 },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
expect(generateOptions(appLocales, currentLocale, localizations, permissions)).toEqual(
|
|
33
|
+
expected
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should remove the locales that are not contained in the localizations array', () => {
|
|
38
|
+
const permissions = [
|
|
39
|
+
{ properties: { locales: [] } },
|
|
40
|
+
{ properties: { locales: ['en', 'fr', 'it'] } },
|
|
41
|
+
];
|
|
42
|
+
const localizations = [
|
|
43
|
+
{ published_at: 'test', locale: 'en', id: 1 },
|
|
44
|
+
{ published_at: 'test', locale: 'fr', id: 2 },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const expected = [
|
|
48
|
+
{ label: 'English', value: 1 },
|
|
49
|
+
{ label: 'French', value: 2 },
|
|
50
|
+
];
|
|
51
|
+
const currentLocale = 'test';
|
|
52
|
+
expect(generateOptions(appLocales, currentLocale, localizations, permissions)).toEqual(
|
|
53
|
+
expected
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should remove the locales when the user does not have the permission to read', () => {
|
|
58
|
+
const permissions = [
|
|
59
|
+
{ properties: { locales: ['en'] } },
|
|
60
|
+
{ properties: { locales: ['it'] } },
|
|
61
|
+
];
|
|
62
|
+
const currentLocale = 'test';
|
|
63
|
+
const localizations = [
|
|
64
|
+
{ published_at: 'test', locale: 'en', id: 1 },
|
|
65
|
+
{ published_at: 'test', locale: 'fr', id: 2 },
|
|
66
|
+
{ published_at: 'test', locale: 'it', id: 3 },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const expected = [
|
|
70
|
+
{ label: 'English', value: 1 },
|
|
71
|
+
{ label: 'Italian', value: 3 },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
expect(generateOptions(appLocales, currentLocale, localizations, permissions)).toEqual(
|
|
75
|
+
expected
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import testData from './data';
|
|
2
|
+
import removePasswordAndRelationsFieldFromData from '../removePasswordAndRelationsFieldFromData';
|
|
3
|
+
|
|
4
|
+
describe('I18n | Components | CMEditViewCopyLocale | utils', () => {
|
|
5
|
+
describe('removePasswordAndRelationsFieldFromData', () => {
|
|
6
|
+
it('should return an empty object', () => {
|
|
7
|
+
const { components, contentType } = testData;
|
|
8
|
+
|
|
9
|
+
expect(removePasswordAndRelationsFieldFromData({}, contentType, components)).toEqual({});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should return the initial data if there is no password field', () => {
|
|
13
|
+
const { components, contentType } = testData;
|
|
14
|
+
|
|
15
|
+
expect(
|
|
16
|
+
removePasswordAndRelationsFieldFromData({ name: 'test' }, contentType, components)
|
|
17
|
+
).toEqual({
|
|
18
|
+
name: 'test',
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should remove the password field for a simple data structure', () => {
|
|
23
|
+
const { components, contentType } = testData;
|
|
24
|
+
const data = { name: 'test', password: 'password' };
|
|
25
|
+
const expected = { name: 'test' };
|
|
26
|
+
|
|
27
|
+
expect(removePasswordAndRelationsFieldFromData(data, contentType, components)).toEqual(
|
|
28
|
+
expected
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should remove all password fields', () => {
|
|
33
|
+
const { components, contentType, modifiedData, expectedModifiedData } = testData;
|
|
34
|
+
|
|
35
|
+
expect(
|
|
36
|
+
removePasswordAndRelationsFieldFromData(modifiedData, contentType, components)
|
|
37
|
+
).toEqual(expectedModifiedData);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import get from 'lodash/get';
|
|
3
|
+
import { useSelector } from 'react-redux';
|
|
4
|
+
import { useParams } from 'react-router-dom';
|
|
5
|
+
import { useContentManagerEditViewDataManager, useQueryParams } from 'strapi-helper-plugin';
|
|
6
|
+
import selectI18NLocales from '../../selectors/selectI18nLocales';
|
|
7
|
+
import useContentTypePermissions from '../../hooks/useContentTypePermissions';
|
|
8
|
+
import CMEditViewLocalePicker from '../CMEditViewLocalePicker';
|
|
9
|
+
|
|
10
|
+
const CMEditViewInjectedComponents = () => {
|
|
11
|
+
const { layout, modifiedData, slug, isSingleType } = useContentManagerEditViewDataManager();
|
|
12
|
+
const { createPermissions, readPermissions } = useContentTypePermissions(slug);
|
|
13
|
+
const locales = useSelector(selectI18NLocales);
|
|
14
|
+
const params = useParams();
|
|
15
|
+
const [{ query }, setQuery] = useQueryParams();
|
|
16
|
+
|
|
17
|
+
const id = get(params, 'id', null);
|
|
18
|
+
const currentEntityId = id;
|
|
19
|
+
const defaultLocale = locales.find(loc => loc.isDefault);
|
|
20
|
+
const currentLocale = get(query, 'plugins.i18n.locale', defaultLocale.code);
|
|
21
|
+
const hasI18nEnabled = get(layout, ['pluginOptions', 'i18n', 'localized'], false);
|
|
22
|
+
const hasDraftAndPublishEnabled = get(layout, ['options', 'draftAndPublish'], false);
|
|
23
|
+
|
|
24
|
+
const defaultQuery = useMemo(() => {
|
|
25
|
+
if (!query) {
|
|
26
|
+
return { plugins: { i18n: { locale: currentLocale } } };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return query;
|
|
30
|
+
}, [query, currentLocale]);
|
|
31
|
+
|
|
32
|
+
if (!hasI18nEnabled) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!currentLocale) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const localizations = get(modifiedData, 'localizations', []);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<CMEditViewLocalePicker
|
|
44
|
+
appLocales={locales}
|
|
45
|
+
currentEntityId={currentEntityId}
|
|
46
|
+
createPermissions={createPermissions}
|
|
47
|
+
hasDraftAndPublishEnabled={hasDraftAndPublishEnabled}
|
|
48
|
+
localizations={localizations}
|
|
49
|
+
isSingleType={isSingleType}
|
|
50
|
+
query={defaultQuery}
|
|
51
|
+
readPermissions={readPermissions}
|
|
52
|
+
setQuery={setQuery}
|
|
53
|
+
slug={slug}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default CMEditViewInjectedComponents;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { components } from 'react-select';
|
|
4
|
+
import { useIntl } from 'react-intl';
|
|
5
|
+
import PropTypes from 'prop-types';
|
|
6
|
+
import get from 'lodash/get';
|
|
7
|
+
import { Flex, Text } from '@buffetjs/core';
|
|
8
|
+
import { RelationDPState } from 'strapi-helper-plugin';
|
|
9
|
+
import { getTrad } from '../../utils';
|
|
10
|
+
|
|
11
|
+
const TextGrow = styled(Text)`
|
|
12
|
+
flex-grow: 2;
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
const statusToTitleMap = {
|
|
16
|
+
draft: 'content-manager.components.Select.draft-info-title',
|
|
17
|
+
published: 'content-manager.components.Select.publish-info-title',
|
|
18
|
+
'did-not-create-locale': getTrad('components.Select.locales.not-available'),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const Option = props => {
|
|
22
|
+
const { formatMessage } = useIntl();
|
|
23
|
+
const Component = components.Option;
|
|
24
|
+
const options = get(props, ['selectProps', 'options'], {});
|
|
25
|
+
const currentOption = options.find(option => option.value === props.value);
|
|
26
|
+
const titleLabelID = statusToTitleMap[currentOption.status];
|
|
27
|
+
const title = formatMessage({ id: titleLabelID });
|
|
28
|
+
const fontWeight = props.isFocused ? 'bold' : 'regular';
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Component {...props}>
|
|
32
|
+
<Flex>
|
|
33
|
+
<RelationDPState
|
|
34
|
+
{...currentOption}
|
|
35
|
+
marginLeft="0"
|
|
36
|
+
marginTop="1px"
|
|
37
|
+
marginRight="10px"
|
|
38
|
+
marginBottom="0"
|
|
39
|
+
title={title}
|
|
40
|
+
/>
|
|
41
|
+
|
|
42
|
+
<TextGrow ellipsis as="div" fontWeight={fontWeight} title={props.label}>
|
|
43
|
+
{props.label}
|
|
44
|
+
</TextGrow>
|
|
45
|
+
</Flex>
|
|
46
|
+
</Component>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
Option.defaultProps = {};
|
|
51
|
+
|
|
52
|
+
Option.propTypes = {
|
|
53
|
+
label: PropTypes.string.isRequired,
|
|
54
|
+
isFocused: PropTypes.bool.isRequired,
|
|
55
|
+
selectProps: PropTypes.shape({
|
|
56
|
+
options: PropTypes.arrayOf(
|
|
57
|
+
PropTypes.shape({
|
|
58
|
+
status: PropTypes.string.isRequired,
|
|
59
|
+
value: PropTypes.string.isRequired,
|
|
60
|
+
}).isRequired
|
|
61
|
+
).isRequired,
|
|
62
|
+
}).isRequired,
|
|
63
|
+
value: PropTypes.string.isRequired,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default Option;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Label, Text, Padded } from '@buffetjs/core';
|
|
4
|
+
import get from 'lodash/get';
|
|
5
|
+
import Select, { components } from 'react-select';
|
|
6
|
+
import { useIntl } from 'react-intl';
|
|
7
|
+
import { useTheme } from 'styled-components';
|
|
8
|
+
import { DropdownIndicator, BaselineAlignment, selectStyles } from 'strapi-helper-plugin';
|
|
9
|
+
import { useHistory } from 'react-router-dom';
|
|
10
|
+
import { stringify } from 'qs';
|
|
11
|
+
import { getTrad } from '../../utils';
|
|
12
|
+
import { addStatusColorToLocale, createLocalesOption } from './utils';
|
|
13
|
+
import CMEditViewCopyLocale from '../CMEditViewCopyLocale';
|
|
14
|
+
import OptionComponent from './Option';
|
|
15
|
+
import Wrapper from './Wrapper';
|
|
16
|
+
|
|
17
|
+
const CMEditViewLocalePicker = ({
|
|
18
|
+
appLocales,
|
|
19
|
+
createPermissions,
|
|
20
|
+
currentEntityId,
|
|
21
|
+
hasDraftAndPublishEnabled,
|
|
22
|
+
isSingleType,
|
|
23
|
+
localizations,
|
|
24
|
+
query,
|
|
25
|
+
readPermissions,
|
|
26
|
+
setQuery,
|
|
27
|
+
slug,
|
|
28
|
+
}) => {
|
|
29
|
+
const { formatMessage } = useIntl();
|
|
30
|
+
const theme = useTheme();
|
|
31
|
+
const currentLocale = get(query, 'plugins.i18n.locale', false);
|
|
32
|
+
const { push } = useHistory();
|
|
33
|
+
|
|
34
|
+
const handleChange = ({ status, value, id }) => {
|
|
35
|
+
let defaultParams = {
|
|
36
|
+
plugins: {
|
|
37
|
+
...query.plugins,
|
|
38
|
+
i18n: { ...query.plugins.i18n, locale: value },
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (currentEntityId) {
|
|
43
|
+
defaultParams.plugins.i18n.relatedEntityId = currentEntityId;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isSingleType) {
|
|
47
|
+
setQuery(defaultParams);
|
|
48
|
+
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (status === 'did-not-create-locale') {
|
|
53
|
+
push({
|
|
54
|
+
pathname: `/plugins/content-manager/collectionType/${slug}/create`,
|
|
55
|
+
search: stringify(defaultParams, { encode: false }),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
push({
|
|
62
|
+
pathname: `/plugins/content-manager/collectionType/${slug}/${id}`,
|
|
63
|
+
search: stringify(defaultParams, { encode: false }),
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const styles = selectStyles(theme);
|
|
68
|
+
|
|
69
|
+
const options = addStatusColorToLocale(
|
|
70
|
+
createLocalesOption(appLocales, localizations),
|
|
71
|
+
theme
|
|
72
|
+
).filter(({ status, value }) => {
|
|
73
|
+
if (status === 'did-not-create-locale') {
|
|
74
|
+
return createPermissions.find(({ properties }) =>
|
|
75
|
+
get(properties, 'locales', []).includes(value)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return readPermissions.find(({ properties }) => get(properties, 'locales', []).includes(value));
|
|
80
|
+
});
|
|
81
|
+
const filteredOptions = options.filter(({ value }) => value !== currentLocale);
|
|
82
|
+
const value = options.find(({ value }) => {
|
|
83
|
+
return value === currentLocale;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const Option = hasDraftAndPublishEnabled ? OptionComponent : components.Option;
|
|
87
|
+
const paddingBottom = localizations.length ? '19px' : '29px';
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Wrapper paddingBottom={paddingBottom}>
|
|
91
|
+
<BaselineAlignment top size="18px" />
|
|
92
|
+
<Padded left right size="smd">
|
|
93
|
+
<Text fontWeight="bold">
|
|
94
|
+
{formatMessage({ id: getTrad('plugin.name'), defaultMessage: 'Internationalization' })}
|
|
95
|
+
</Text>
|
|
96
|
+
<BaselineAlignment top size="18px" />
|
|
97
|
+
<span id="select-locale">
|
|
98
|
+
<Label htmlFor="">
|
|
99
|
+
{formatMessage({
|
|
100
|
+
id: getTrad('Settings.locales.modal.locales.label'),
|
|
101
|
+
})}
|
|
102
|
+
</Label>
|
|
103
|
+
</span>
|
|
104
|
+
<BaselineAlignment top size="3px" />
|
|
105
|
+
<Select
|
|
106
|
+
aria-labelledby="select-locale"
|
|
107
|
+
components={{ DropdownIndicator, Option }}
|
|
108
|
+
isSearchable={false}
|
|
109
|
+
onChange={handleChange}
|
|
110
|
+
options={filteredOptions}
|
|
111
|
+
styles={{
|
|
112
|
+
...styles,
|
|
113
|
+
control: (base, state) => ({ ...base, ...styles.control(base, state), height: '34px' }),
|
|
114
|
+
indicatorsContainer: (base, state) => ({
|
|
115
|
+
...base,
|
|
116
|
+
...styles.indicatorsContainer(base, state),
|
|
117
|
+
height: '32px',
|
|
118
|
+
}),
|
|
119
|
+
valueContainer: base => ({
|
|
120
|
+
...base,
|
|
121
|
+
padding: '2px 0px 4px 10px',
|
|
122
|
+
lineHeight: '18px',
|
|
123
|
+
}),
|
|
124
|
+
}}
|
|
125
|
+
value={value}
|
|
126
|
+
/>
|
|
127
|
+
<CMEditViewCopyLocale
|
|
128
|
+
appLocales={appLocales}
|
|
129
|
+
currentLocale={currentLocale}
|
|
130
|
+
localizations={localizations}
|
|
131
|
+
readPermissions={readPermissions}
|
|
132
|
+
/>
|
|
133
|
+
</Padded>
|
|
134
|
+
</Wrapper>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
CMEditViewLocalePicker.defaultProps = {
|
|
139
|
+
createPermissions: [],
|
|
140
|
+
currentEntityId: null,
|
|
141
|
+
isSingleType: false,
|
|
142
|
+
localizations: [],
|
|
143
|
+
query: {},
|
|
144
|
+
readPermissions: [],
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
CMEditViewLocalePicker.propTypes = {
|
|
148
|
+
appLocales: PropTypes.array.isRequired,
|
|
149
|
+
createPermissions: PropTypes.array,
|
|
150
|
+
currentEntityId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
151
|
+
hasDraftAndPublishEnabled: PropTypes.bool.isRequired,
|
|
152
|
+
isSingleType: PropTypes.bool,
|
|
153
|
+
localizations: PropTypes.array,
|
|
154
|
+
query: PropTypes.object,
|
|
155
|
+
readPermissions: PropTypes.array,
|
|
156
|
+
setQuery: PropTypes.func.isRequired,
|
|
157
|
+
slug: PropTypes.string.isRequired,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export default CMEditViewLocalePicker;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const addStatusColorToLocale = (locales, theme) =>
|
|
2
|
+
locales.map(({ status, ...rest }) => {
|
|
3
|
+
const statusMap = {
|
|
4
|
+
'did-not-create-locale': {
|
|
5
|
+
backgroundColor: theme.main.colors.white,
|
|
6
|
+
border: `1px solid ${theme.main.colors.grey}`,
|
|
7
|
+
},
|
|
8
|
+
draft: {
|
|
9
|
+
backgroundColor: theme.main.colors.mediumBlue,
|
|
10
|
+
},
|
|
11
|
+
published: {
|
|
12
|
+
backgroundColor: theme.main.colors.green,
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
const props = statusMap[status];
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
...props,
|
|
19
|
+
status,
|
|
20
|
+
...rest,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export default addStatusColorToLocale;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const createLocalesOption = (localesToDisplay, localesFromData) => {
|
|
2
|
+
return localesToDisplay.map(({ name, code }) => {
|
|
3
|
+
const matchingLocaleInData = localesFromData.find(({ locale }) => locale === code);
|
|
4
|
+
|
|
5
|
+
let status = 'did-not-create-locale';
|
|
6
|
+
|
|
7
|
+
if (matchingLocaleInData) {
|
|
8
|
+
status = matchingLocaleInData.published_at === null ? 'draft' : 'published';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
id: matchingLocaleInData ? matchingLocaleInData.id : null,
|
|
13
|
+
label: name,
|
|
14
|
+
value: code,
|
|
15
|
+
status,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default createLocalesOption;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useIntl } from 'react-intl';
|
|
4
|
+
import { Checkbox, Text } from '@buffetjs/core';
|
|
5
|
+
import { ModalConfirm } from 'strapi-helper-plugin';
|
|
6
|
+
import { getTrad } from '../../utils';
|
|
7
|
+
import Wrapper from './Wrapper';
|
|
8
|
+
|
|
9
|
+
const CheckboxConfirmation = ({ description, isCreating, label, name, onChange, ...rest }) => {
|
|
10
|
+
const { formatMessage } = useIntl();
|
|
11
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
12
|
+
|
|
13
|
+
const handleChange = e => {
|
|
14
|
+
if (isCreating || e.target.value) {
|
|
15
|
+
return onChange(e);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!e.target.value) {
|
|
19
|
+
return setIsOpen(true);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const handleConfirm = () => {
|
|
26
|
+
onChange({ target: { name, value: false, type: 'checkbox' } });
|
|
27
|
+
setIsOpen(false);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const handleToggle = () => setIsOpen(prev => !prev);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<Wrapper>
|
|
35
|
+
<Checkbox {...rest} message={label} name={name} onChange={handleChange} type="checkbox" />
|
|
36
|
+
{description && (
|
|
37
|
+
<Text color="grey" title={description} fontSize="sm" ellipsis>
|
|
38
|
+
{description}
|
|
39
|
+
</Text>
|
|
40
|
+
)}
|
|
41
|
+
</Wrapper>
|
|
42
|
+
<ModalConfirm
|
|
43
|
+
confirmButtonLabel={{ id: getTrad('CheckboxConfirmation.Modal.button-confirm') }}
|
|
44
|
+
content={{ id: getTrad('CheckboxConfirmation.Modal.content') }}
|
|
45
|
+
isOpen={isOpen}
|
|
46
|
+
toggle={handleToggle}
|
|
47
|
+
onConfirm={handleConfirm}
|
|
48
|
+
>
|
|
49
|
+
<Text fontWeight="bold">
|
|
50
|
+
{formatMessage({ id: getTrad('CheckboxConfirmation.Modal.body') })}
|
|
51
|
+
</Text>
|
|
52
|
+
</ModalConfirm>
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
CheckboxConfirmation.defaultProps = {
|
|
58
|
+
description: null,
|
|
59
|
+
isCreating: false,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
CheckboxConfirmation.propTypes = {
|
|
63
|
+
description: PropTypes.string,
|
|
64
|
+
label: PropTypes.string.isRequired,
|
|
65
|
+
isCreating: PropTypes.bool,
|
|
66
|
+
name: PropTypes.string.isRequired,
|
|
67
|
+
onChange: PropTypes.func.isRequired,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default CheckboxConfirmation;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { FormattedMessage } from 'react-intl';
|
|
3
|
+
import { getTrad } from '../../utils';
|
|
4
|
+
import useHasI18n from '../../hooks/useHasI18n';
|
|
5
|
+
|
|
6
|
+
const DeleteModalAdditionalInfos = () => {
|
|
7
|
+
const hasI18nEnabled = useHasI18n();
|
|
8
|
+
|
|
9
|
+
if (!hasI18nEnabled) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<span>
|
|
15
|
+
<FormattedMessage
|
|
16
|
+
id={getTrad('Settings.list.actions.deleteAdditionalInfos')}
|
|
17
|
+
values={{
|
|
18
|
+
em: chunks => <em>{chunks}</em>,
|
|
19
|
+
}}
|
|
20
|
+
/>
|
|
21
|
+
</span>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default DeleteModalAdditionalInfos;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import { EmptyState, ListButton } from 'strapi-helper-plugin';
|
|
4
|
+
import { List } from '@buffetjs/custom';
|
|
5
|
+
import { Button } from '@buffetjs/core';
|
|
6
|
+
import { Plus } from '@buffetjs/icons';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import useLocales from '../../hooks/useLocales';
|
|
9
|
+
import LocaleRow from '../LocaleRow';
|
|
10
|
+
import { getTrad } from '../../utils';
|
|
11
|
+
import ModalEdit from '../ModalEdit';
|
|
12
|
+
import ModalDelete from '../ModalDelete';
|
|
13
|
+
import ModalCreate from '../ModalCreate';
|
|
14
|
+
|
|
15
|
+
const LocaleList = ({ canUpdateLocale, canDeleteLocale, onToggleCreateModal, isCreating }) => {
|
|
16
|
+
const [localeToDelete, setLocaleToDelete] = useState();
|
|
17
|
+
const [localeToEdit, setLocaleToEdit] = useState();
|
|
18
|
+
const { locales, isLoading } = useLocales();
|
|
19
|
+
const { formatMessage } = useIntl();
|
|
20
|
+
|
|
21
|
+
// Delete actions
|
|
22
|
+
const closeModalToDelete = () => setLocaleToDelete(undefined);
|
|
23
|
+
const handleDeleteLocale = canDeleteLocale ? setLocaleToDelete : undefined;
|
|
24
|
+
|
|
25
|
+
// Edit actions
|
|
26
|
+
const closeModalToEdit = () => {
|
|
27
|
+
setLocaleToEdit(undefined);
|
|
28
|
+
};
|
|
29
|
+
const handleEditLocale = canUpdateLocale ? setLocaleToEdit : undefined;
|
|
30
|
+
|
|
31
|
+
if (isLoading || (locales && locales.length > 0)) {
|
|
32
|
+
const listTitle = isLoading
|
|
33
|
+
? null
|
|
34
|
+
: formatMessage(
|
|
35
|
+
{
|
|
36
|
+
id: getTrad(
|
|
37
|
+
`Settings.locales.list.title${locales.length > 1 ? '.plural' : '.singular'}`
|
|
38
|
+
),
|
|
39
|
+
},
|
|
40
|
+
{ number: locales.length }
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<List
|
|
46
|
+
radius="2px"
|
|
47
|
+
title={listTitle}
|
|
48
|
+
items={locales}
|
|
49
|
+
isLoading={isLoading}
|
|
50
|
+
customRowComponent={locale => (
|
|
51
|
+
<LocaleRow locale={locale} onDelete={handleDeleteLocale} onEdit={handleEditLocale} />
|
|
52
|
+
)}
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
<ModalCreate
|
|
56
|
+
isOpened={isCreating}
|
|
57
|
+
onClose={onToggleCreateModal}
|
|
58
|
+
alreadyUsedLocales={locales}
|
|
59
|
+
/>
|
|
60
|
+
<ModalDelete localeToDelete={localeToDelete} onClose={closeModalToDelete} />
|
|
61
|
+
<ModalEdit localeToEdit={localeToEdit} onClose={closeModalToEdit} locales={locales} />
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<EmptyState
|
|
69
|
+
title={formatMessage({ id: getTrad('Settings.list.empty.title') })}
|
|
70
|
+
description={formatMessage({ id: getTrad('Settings.list.empty.description') })}
|
|
71
|
+
/>
|
|
72
|
+
|
|
73
|
+
{onToggleCreateModal && (
|
|
74
|
+
<ListButton>
|
|
75
|
+
<Button
|
|
76
|
+
label={formatMessage({ id: getTrad('Settings.list.actions.add') })}
|
|
77
|
+
onClick={onToggleCreateModal}
|
|
78
|
+
color="primary"
|
|
79
|
+
type="button"
|
|
80
|
+
icon={<Plus fill="#007eff" width="11px" height="11px" />}
|
|
81
|
+
/>
|
|
82
|
+
</ListButton>
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
<ModalCreate isOpened={isCreating} onClose={onToggleCreateModal} />
|
|
86
|
+
</>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
LocaleList.defaultProps = {
|
|
91
|
+
onToggleCreateModal: undefined,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
LocaleList.propTypes = {
|
|
95
|
+
canUpdateLocale: PropTypes.bool.isRequired,
|
|
96
|
+
canDeleteLocale: PropTypes.bool.isRequired,
|
|
97
|
+
onToggleCreateModal: PropTypes.func,
|
|
98
|
+
isCreating: PropTypes.bool.isRequired,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default LocaleList;
|