@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,205 @@
|
|
|
1
|
+
import mutateSchema, {
|
|
2
|
+
addLocalisationToFields,
|
|
3
|
+
disableAttributesLocalisation,
|
|
4
|
+
} from '../mutateCTBContentTypeSchema';
|
|
5
|
+
|
|
6
|
+
describe('i18n | utils ', () => {
|
|
7
|
+
describe('mutateCTBContentTypeSchema', () => {
|
|
8
|
+
it('should forward the data the pluginOptions.i18n.localized key does not exist in the content type', () => {
|
|
9
|
+
const data = { pluginOptions: { test: true } };
|
|
10
|
+
|
|
11
|
+
expect(mutateSchema(data)).toEqual(data);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should remove the pluginOptions.i18n key from the content type schema', () => {
|
|
15
|
+
const ctSchema = {
|
|
16
|
+
pluginOptions: {
|
|
17
|
+
pluginA: { foo: 'bar' },
|
|
18
|
+
i18n: { localized: false },
|
|
19
|
+
pluginB: { foo: 'bar' },
|
|
20
|
+
},
|
|
21
|
+
kind: 'test',
|
|
22
|
+
attributes: {
|
|
23
|
+
one: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
pluginOptions: {
|
|
26
|
+
i18n: { localized: true },
|
|
27
|
+
},
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
two: {
|
|
31
|
+
type: 'number',
|
|
32
|
+
pluginOptions: {
|
|
33
|
+
pluginA: { test: true },
|
|
34
|
+
i18n: { localized: true },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const expected = {
|
|
41
|
+
pluginOptions: {
|
|
42
|
+
pluginA: { foo: 'bar' },
|
|
43
|
+
pluginB: { foo: 'bar' },
|
|
44
|
+
},
|
|
45
|
+
kind: 'test',
|
|
46
|
+
attributes: {
|
|
47
|
+
one: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
pluginOptions: {},
|
|
50
|
+
required: true,
|
|
51
|
+
},
|
|
52
|
+
two: {
|
|
53
|
+
type: 'number',
|
|
54
|
+
pluginOptions: {
|
|
55
|
+
pluginA: { test: true },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
expect(mutateSchema(ctSchema, {})).toEqual(expected);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should return the data if the initial schema already has i18n enabled', () => {
|
|
65
|
+
const ctSchema = {
|
|
66
|
+
pluginOptions: {
|
|
67
|
+
pluginA: { foo: 'bar' },
|
|
68
|
+
i18n: { localized: true },
|
|
69
|
+
pluginB: { foo: 'bar' },
|
|
70
|
+
},
|
|
71
|
+
kind: 'test',
|
|
72
|
+
attributes: {
|
|
73
|
+
one: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
pluginOptions: {
|
|
76
|
+
i18n: { localized: true },
|
|
77
|
+
},
|
|
78
|
+
required: true,
|
|
79
|
+
},
|
|
80
|
+
two: {
|
|
81
|
+
type: 'number',
|
|
82
|
+
pluginOptions: {
|
|
83
|
+
pluginA: { test: true },
|
|
84
|
+
i18n: { localized: true },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
expect(
|
|
91
|
+
mutateSchema(ctSchema, {
|
|
92
|
+
schema: {
|
|
93
|
+
pluginOptions: {
|
|
94
|
+
pluginA: { foo: 'bar' },
|
|
95
|
+
i18n: { localized: true },
|
|
96
|
+
pluginB: { foo: 'bar' },
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
).toEqual(ctSchema);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should set the pluginOptions.i18n.localized to true an all attributes', () => {
|
|
104
|
+
const nextSchema = {
|
|
105
|
+
pluginOptions: { pluginA: { ok: true }, i18n: { localized: true } },
|
|
106
|
+
attributes: {
|
|
107
|
+
cover: { type: 'media', pluginOptions: { pluginA: { ok: true } } },
|
|
108
|
+
name: {
|
|
109
|
+
type: 'text',
|
|
110
|
+
pluginOptions: { pluginA: { ok: true }, i18n: { localized: false } },
|
|
111
|
+
},
|
|
112
|
+
price: {
|
|
113
|
+
type: 'text',
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
const expected = {
|
|
118
|
+
pluginOptions: { pluginA: { ok: true }, i18n: { localized: true } },
|
|
119
|
+
attributes: {
|
|
120
|
+
cover: {
|
|
121
|
+
type: 'media',
|
|
122
|
+
pluginOptions: { pluginA: { ok: true }, i18n: { localized: true } },
|
|
123
|
+
},
|
|
124
|
+
name: {
|
|
125
|
+
type: 'text',
|
|
126
|
+
pluginOptions: { pluginA: { ok: true }, i18n: { localized: true } },
|
|
127
|
+
},
|
|
128
|
+
price: {
|
|
129
|
+
type: 'text',
|
|
130
|
+
pluginOptions: { i18n: { localized: true } },
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
expect(mutateSchema(nextSchema, {})).toEqual(expected);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('i18n addLocalisationToFields', () => {
|
|
140
|
+
it('should forward the data if the attribute type is not correct', () => {
|
|
141
|
+
const attributes = {
|
|
142
|
+
foo: { type: 'relation' },
|
|
143
|
+
bar: { type: 'custom' },
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
expect(addLocalisationToFields(attributes)).toEqual(attributes);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should keep the pluginOptions for each attribute and enable the i18n.localized value', () => {
|
|
150
|
+
const attributes = {
|
|
151
|
+
foo: { type: 'text', pluginOptions: { pluginA: { ok: true } }, required: true },
|
|
152
|
+
bar: { type: 'text', pluginOptions: { i18n: { localized: false } } },
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const expected = {
|
|
156
|
+
foo: {
|
|
157
|
+
type: 'text',
|
|
158
|
+
pluginOptions: { pluginA: { ok: true }, i18n: { localized: true } },
|
|
159
|
+
required: true,
|
|
160
|
+
},
|
|
161
|
+
bar: { type: 'text', pluginOptions: { i18n: { localized: true } } },
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
expect(addLocalisationToFields(attributes)).toEqual(expected);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should enable the pluginOptions.i18n.localized value for each attribute', () => {
|
|
168
|
+
const attributes = {
|
|
169
|
+
foo: { type: 'text', required: true },
|
|
170
|
+
bar: { type: 'text' },
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const expected = {
|
|
174
|
+
foo: {
|
|
175
|
+
type: 'text',
|
|
176
|
+
pluginOptions: { i18n: { localized: true } },
|
|
177
|
+
required: true,
|
|
178
|
+
},
|
|
179
|
+
bar: { type: 'text', pluginOptions: { i18n: { localized: true } } },
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
expect(addLocalisationToFields(attributes)).toEqual(expected);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('disableAttributesLocalisation', () => {
|
|
187
|
+
it('should remove the pluginOptions.i18n for all attributes', () => {
|
|
188
|
+
const attributes = {
|
|
189
|
+
foo: {
|
|
190
|
+
type: 'text',
|
|
191
|
+
pluginOptions: { pluginA: { ok: true }, i18n: { localized: true } },
|
|
192
|
+
required: true,
|
|
193
|
+
},
|
|
194
|
+
bar: { type: 'text', pluginOptions: { i18n: { localized: true } } },
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const expected = {
|
|
198
|
+
foo: { type: 'text', required: true, pluginOptions: { pluginA: { ok: true } } },
|
|
199
|
+
bar: { type: 'text', pluginOptions: {} },
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
expect(disableAttributesLocalisation(attributes)).toEqual(expected);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getService } = require('../../utils');
|
|
4
|
+
|
|
5
|
+
module.exports = async () => {
|
|
6
|
+
const { sendDidInitializeEvent } = getService('metrics');
|
|
7
|
+
const { decorator } = getService('entity-service-decorator');
|
|
8
|
+
const { initDefaultLocale } = getService('locales');
|
|
9
|
+
const { sectionsBuilder, actions, engine } = getService('permissions');
|
|
10
|
+
|
|
11
|
+
// Entity Service
|
|
12
|
+
strapi.entityService.decorate(decorator);
|
|
13
|
+
|
|
14
|
+
// Data
|
|
15
|
+
await initDefaultLocale();
|
|
16
|
+
|
|
17
|
+
// Sections Builder
|
|
18
|
+
sectionsBuilder.registerLocalesPropertyHandler();
|
|
19
|
+
|
|
20
|
+
// Actions
|
|
21
|
+
await actions.registerI18nActions();
|
|
22
|
+
actions.registerI18nActionsHooks();
|
|
23
|
+
actions.updateActionsProperties();
|
|
24
|
+
|
|
25
|
+
// Engine/Permissions
|
|
26
|
+
engine.registerI18nPermissionsHandlers();
|
|
27
|
+
|
|
28
|
+
// Hooks & Models
|
|
29
|
+
registerModelsHooks();
|
|
30
|
+
|
|
31
|
+
sendDidInitializeEvent();
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const registerModelsHooks = () => {
|
|
35
|
+
Object.values(strapi.models)
|
|
36
|
+
.filter(model => getService('content-types').isLocalizedContentType(model))
|
|
37
|
+
.forEach(model => {
|
|
38
|
+
strapi.db.lifecycles.register({
|
|
39
|
+
model: model.uid,
|
|
40
|
+
async beforeCreate(data) {
|
|
41
|
+
await getService('localizations').assignDefaultLocale(data);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
strapi.db.lifecycles.register({
|
|
47
|
+
model: 'plugins::i18n.locale',
|
|
48
|
+
|
|
49
|
+
async afterCreate() {
|
|
50
|
+
await getService('permissions').actions.syncSuperAdminPermissionsWithLocales();
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async afterDelete() {
|
|
54
|
+
await getService('permissions').actions.syncSuperAdminPermissionsWithLocales();
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
};
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { after } = require('../content-type/enable');
|
|
4
|
+
const { before } = require('../content-type/disable');
|
|
5
|
+
const ctService = require('../../../../services/content-types');
|
|
6
|
+
|
|
7
|
+
describe('i18n - Migration - enable/disable localization on a CT', () => {
|
|
8
|
+
beforeAll(() => {
|
|
9
|
+
global.strapi = {
|
|
10
|
+
plugins: { i18n: { services: { 'content-types': ctService } } },
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('enable localization on a CT', () => {
|
|
15
|
+
describe('Should not migrate', () => {
|
|
16
|
+
test('non i18n => non i18n', async () => {
|
|
17
|
+
const previousDefinition = {};
|
|
18
|
+
const definition = {};
|
|
19
|
+
|
|
20
|
+
await after({ definition, previousDefinition });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('i18n => non i18n', async () => {
|
|
24
|
+
const previousDefinition = { pluginOptions: { i18n: { localized: true } } };
|
|
25
|
+
const definition = {};
|
|
26
|
+
|
|
27
|
+
await after({ definition, previousDefinition });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('i18n => i18n', async () => {
|
|
31
|
+
const previousDefinition = { pluginOptions: { i18n: { localized: true } } };
|
|
32
|
+
const definition = { pluginOptions: { i18n: { localized: true } } };
|
|
33
|
+
|
|
34
|
+
await after({ definition, previousDefinition });
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('Should migrate', () => {
|
|
38
|
+
describe('bookshelf', () => {
|
|
39
|
+
test('non i18n => i18n - default locale in core_store', async () => {
|
|
40
|
+
const model = { orm: 'bookshelf' };
|
|
41
|
+
const previousDefinition = {};
|
|
42
|
+
const definition = { pluginOptions: { i18n: { localized: true } } };
|
|
43
|
+
const defaultLocaleRows = [{ value: '"fr"' }];
|
|
44
|
+
const where = jest.fn().mockReturnValueOnce(Promise.resolve(defaultLocaleRows));
|
|
45
|
+
|
|
46
|
+
const knex = { where };
|
|
47
|
+
knex.select = () => knex;
|
|
48
|
+
knex.from = () => knex;
|
|
49
|
+
knex.update = jest.fn(() => knex);
|
|
50
|
+
|
|
51
|
+
const ORM = { knex };
|
|
52
|
+
|
|
53
|
+
await after({ model, definition, previousDefinition, ORM });
|
|
54
|
+
|
|
55
|
+
expect(knex.update).toHaveBeenCalledWith({ locale: 'fr' });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('non i18n => i18n - default locale not in core_store', async () => {
|
|
59
|
+
const model = { orm: 'bookshelf' };
|
|
60
|
+
const previousDefinition = {};
|
|
61
|
+
const definition = { pluginOptions: { i18n: { localized: true } } };
|
|
62
|
+
const defaultLocaleRows = [];
|
|
63
|
+
const where = jest.fn().mockReturnValueOnce(Promise.resolve(defaultLocaleRows));
|
|
64
|
+
|
|
65
|
+
const knex = { where };
|
|
66
|
+
knex.select = () => knex;
|
|
67
|
+
knex.from = () => knex;
|
|
68
|
+
knex.update = jest.fn(() => knex);
|
|
69
|
+
|
|
70
|
+
const ORM = { knex };
|
|
71
|
+
|
|
72
|
+
await after({ model, definition, previousDefinition, ORM });
|
|
73
|
+
|
|
74
|
+
expect(knex.update).toHaveBeenCalledWith({ locale: 'en' });
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('mongoose', () => {
|
|
78
|
+
test('non i18n => i18n - default locale in core_store', async () => {
|
|
79
|
+
const previousDefinition = {};
|
|
80
|
+
const definition = { pluginOptions: { i18n: { localized: true } } };
|
|
81
|
+
const defaultLocaleRows = [{ value: '"fr"' }];
|
|
82
|
+
const find = jest.fn(() => Promise.resolve(defaultLocaleRows));
|
|
83
|
+
const updateMany = jest.fn();
|
|
84
|
+
const model = { orm: 'mongoose', updateMany };
|
|
85
|
+
global.strapi.models = { core_store: { find } };
|
|
86
|
+
|
|
87
|
+
await after({ model, definition, previousDefinition });
|
|
88
|
+
|
|
89
|
+
expect(updateMany).toHaveBeenCalledWith(
|
|
90
|
+
{ $or: [{ locale: { $exists: false } }, { locale: null }] },
|
|
91
|
+
{ locale: 'fr' }
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('non i18n => i18n - default locale not in core_store', async () => {
|
|
96
|
+
const previousDefinition = {};
|
|
97
|
+
const definition = { pluginOptions: { i18n: { localized: true } } };
|
|
98
|
+
const defaultLocaleRows = [];
|
|
99
|
+
const find = jest.fn(() => Promise.resolve(defaultLocaleRows));
|
|
100
|
+
const updateMany = jest.fn();
|
|
101
|
+
const model = { orm: 'mongoose', updateMany };
|
|
102
|
+
global.strapi.models = { core_store: { find } };
|
|
103
|
+
|
|
104
|
+
await after({ model, definition, previousDefinition });
|
|
105
|
+
|
|
106
|
+
expect(updateMany).toHaveBeenCalledWith(
|
|
107
|
+
{ $or: [{ locale: { $exists: false } }, { locale: null }] },
|
|
108
|
+
{ locale: 'en' }
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('disable localization on a CT', () => {
|
|
116
|
+
describe('Should not migrate', () => {
|
|
117
|
+
test('non i18n => non i18n', async () => {
|
|
118
|
+
const previousDefinition = {};
|
|
119
|
+
const definition = {};
|
|
120
|
+
|
|
121
|
+
await before({ definition, previousDefinition });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('non i18n => i18n', async () => {
|
|
125
|
+
const previousDefinition = {};
|
|
126
|
+
const definition = { pluginOptions: { i18n: { localized: true } } };
|
|
127
|
+
|
|
128
|
+
await before({ definition, previousDefinition });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('i18n => i18n', async () => {
|
|
132
|
+
const previousDefinition = { pluginOptions: { i18n: { localized: true } } };
|
|
133
|
+
const definition = { pluginOptions: { i18n: { localized: true } } };
|
|
134
|
+
|
|
135
|
+
await before({ definition, previousDefinition });
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
describe('Should migrate', () => {
|
|
139
|
+
describe('bookshelf', () => {
|
|
140
|
+
test('i18n => non i18n - pg', async () => {
|
|
141
|
+
const previousDefinition = {
|
|
142
|
+
pluginOptions: { i18n: { localized: true } },
|
|
143
|
+
collectionName: 'countries',
|
|
144
|
+
};
|
|
145
|
+
const definition = { client: 'pg' };
|
|
146
|
+
const defaultLocaleRows = [{ value: '"fr"' }];
|
|
147
|
+
const deleteRelations = jest.fn();
|
|
148
|
+
const dropTableIfExists = jest.fn();
|
|
149
|
+
const table = jest.fn();
|
|
150
|
+
const model = { orm: 'bookshelf', deleteRelations };
|
|
151
|
+
const where = jest.fn(() => Promise.resolve(defaultLocaleRows));
|
|
152
|
+
const trx = {
|
|
153
|
+
limit: jest.fn(),
|
|
154
|
+
commit: jest.fn(),
|
|
155
|
+
};
|
|
156
|
+
trx.select = jest.fn(() => trx);
|
|
157
|
+
trx.del = jest.fn(() => trx);
|
|
158
|
+
trx.from = jest.fn(() => trx);
|
|
159
|
+
trx.whereNot = jest.fn().mockReturnValueOnce(trx);
|
|
160
|
+
trx.orderBy = jest.fn(() => trx);
|
|
161
|
+
trx.offset = jest.fn(() => trx);
|
|
162
|
+
trx.limit = jest.fn(() => Promise.resolve([{ id: 1 }, { id: 2 }]));
|
|
163
|
+
const transaction = jest.fn(() => Promise.resolve(trx));
|
|
164
|
+
const knex = { where, transaction, schema: { dropTableIfExists, table } };
|
|
165
|
+
knex.select = jest.fn(() => knex);
|
|
166
|
+
knex.from = jest.fn(() => knex);
|
|
167
|
+
const ORM = { knex };
|
|
168
|
+
|
|
169
|
+
await before({ model, definition, previousDefinition, ORM });
|
|
170
|
+
|
|
171
|
+
expect(deleteRelations).toHaveBeenCalledTimes(2);
|
|
172
|
+
expect(deleteRelations).toHaveBeenNthCalledWith(1, 1, { transacting: trx });
|
|
173
|
+
expect(deleteRelations).toHaveBeenNthCalledWith(2, 2, { transacting: trx });
|
|
174
|
+
expect(trx.del).toHaveBeenCalled();
|
|
175
|
+
expect(trx.whereNot).toHaveBeenNthCalledWith(2, 'locale', 'fr');
|
|
176
|
+
expect(transaction).toHaveBeenCalled();
|
|
177
|
+
expect(trx.commit).toHaveBeenCalled();
|
|
178
|
+
expect(table).toHaveBeenCalled();
|
|
179
|
+
expect(dropTableIfExists).toHaveBeenCalledWith('countries__localizations');
|
|
180
|
+
});
|
|
181
|
+
test('i18n => non i18n - sqlite', async () => {
|
|
182
|
+
const previousDefinition = {
|
|
183
|
+
pluginOptions: { i18n: { localized: true } },
|
|
184
|
+
collectionName: 'countries',
|
|
185
|
+
};
|
|
186
|
+
const definition = { client: 'sqlite3' };
|
|
187
|
+
const defaultLocaleRows = [{ value: '"fr"' }];
|
|
188
|
+
const deleteRelations = jest.fn();
|
|
189
|
+
const dropTableIfExists = jest.fn();
|
|
190
|
+
const table = jest.fn();
|
|
191
|
+
const model = { orm: 'bookshelf', deleteRelations };
|
|
192
|
+
const where = jest.fn(() => Promise.resolve(defaultLocaleRows));
|
|
193
|
+
const trx = {
|
|
194
|
+
limit: jest.fn(),
|
|
195
|
+
commit: jest.fn(),
|
|
196
|
+
};
|
|
197
|
+
trx.select = jest.fn(() => trx);
|
|
198
|
+
trx.del = jest.fn(() => trx);
|
|
199
|
+
trx.from = jest.fn(() => trx);
|
|
200
|
+
trx.whereNot = jest.fn().mockReturnValueOnce(trx);
|
|
201
|
+
trx.orderBy = jest.fn(() => trx);
|
|
202
|
+
trx.offset = jest.fn(() => trx);
|
|
203
|
+
trx.limit = jest.fn(() => Promise.resolve([{ id: 1 }, { id: 2 }]));
|
|
204
|
+
const transaction = jest.fn(() => Promise.resolve(trx));
|
|
205
|
+
const knex = { where, transaction, schema: { dropTableIfExists, table } };
|
|
206
|
+
knex.select = jest.fn(() => knex);
|
|
207
|
+
knex.from = jest.fn(() => knex);
|
|
208
|
+
const ORM = { knex };
|
|
209
|
+
|
|
210
|
+
const context = {};
|
|
211
|
+
await before({ model, definition, previousDefinition, ORM }, context);
|
|
212
|
+
|
|
213
|
+
expect(deleteRelations).toHaveBeenCalledTimes(2);
|
|
214
|
+
expect(deleteRelations).toHaveBeenNthCalledWith(1, 1, { transacting: trx });
|
|
215
|
+
expect(deleteRelations).toHaveBeenNthCalledWith(2, 2, { transacting: trx });
|
|
216
|
+
expect(trx.del).toHaveBeenCalled();
|
|
217
|
+
expect(trx.whereNot).toHaveBeenNthCalledWith(2, 'locale', 'fr');
|
|
218
|
+
expect(transaction).toHaveBeenCalled();
|
|
219
|
+
expect(trx.commit).toHaveBeenCalled();
|
|
220
|
+
expect(table).not.toHaveBeenCalled();
|
|
221
|
+
expect(context).toEqual({ recreateSqliteTable: true });
|
|
222
|
+
expect(dropTableIfExists).toHaveBeenCalledWith('countries__localizations');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
describe('mongoose', () => {
|
|
226
|
+
test('i18n => non i18n', async () => {
|
|
227
|
+
const previousDefinition = { pluginOptions: { i18n: { localized: true } } };
|
|
228
|
+
const definition = {};
|
|
229
|
+
const defaultLocaleRows = [{ value: '"fr"' }];
|
|
230
|
+
const coreStoreFind = jest.fn(() => Promise.resolve(defaultLocaleRows));
|
|
231
|
+
const updateMany = jest.fn();
|
|
232
|
+
const deleteMany = jest.fn();
|
|
233
|
+
const deleteRelations = jest.fn();
|
|
234
|
+
const model = { orm: 'mongoose', updateMany, deleteMany, deleteRelations };
|
|
235
|
+
model.sort = jest.fn(() => model);
|
|
236
|
+
model.find = jest.fn(() => model);
|
|
237
|
+
model.limit = jest.fn(() => Promise.resolve([{ id: 1 }, { id: 2 }]));
|
|
238
|
+
global.strapi.models = { core_store: { find: coreStoreFind } };
|
|
239
|
+
|
|
240
|
+
await before({ model, definition, previousDefinition });
|
|
241
|
+
|
|
242
|
+
expect(deleteRelations).toHaveBeenCalledTimes(2);
|
|
243
|
+
expect(deleteRelations).toHaveBeenNthCalledWith(1, { id: 1 });
|
|
244
|
+
expect(deleteRelations).toHaveBeenNthCalledWith(2, { id: 2 });
|
|
245
|
+
expect(deleteMany).toHaveBeenCalledWith({ locale: { $ne: 'fr' } });
|
|
246
|
+
expect(updateMany).toHaveBeenCalledWith(
|
|
247
|
+
{},
|
|
248
|
+
{ $unset: { locale: '' } },
|
|
249
|
+
{ strict: false }
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { after } = require('../field');
|
|
4
|
+
|
|
5
|
+
describe('i18n - Migration - disable localization on a field', () => {
|
|
6
|
+
describe('after', () => {
|
|
7
|
+
describe('Should not migrate', () => {
|
|
8
|
+
test("Doesn't migrate if model isn't localized", async () => {
|
|
9
|
+
const find = jest.fn();
|
|
10
|
+
global.strapi = {
|
|
11
|
+
query: () => {
|
|
12
|
+
find;
|
|
13
|
+
},
|
|
14
|
+
plugins: {
|
|
15
|
+
i18n: {
|
|
16
|
+
services: {
|
|
17
|
+
'content-types': {
|
|
18
|
+
isLocalizedContentType: () => false,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const model = {};
|
|
26
|
+
const previousDefinition = {};
|
|
27
|
+
|
|
28
|
+
await after({ model, definition: model, previousDefinition });
|
|
29
|
+
expect(find).not.toHaveBeenCalled();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("Doesn't migrate if no attribute changed (without i18n)", async () => {
|
|
33
|
+
const find = jest.fn();
|
|
34
|
+
const getLocalizedAttributes = jest.fn(() => []);
|
|
35
|
+
|
|
36
|
+
global.strapi = {
|
|
37
|
+
query: () => {
|
|
38
|
+
find;
|
|
39
|
+
},
|
|
40
|
+
plugins: {
|
|
41
|
+
i18n: {
|
|
42
|
+
services: {
|
|
43
|
+
'content-types': {
|
|
44
|
+
isLocalizedContentType: () => true,
|
|
45
|
+
getLocalizedAttributes,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const model = { attributes: { name: {} } };
|
|
53
|
+
const previousDefinition = { attributes: { name: {} } };
|
|
54
|
+
|
|
55
|
+
await after({ model, definition: model, previousDefinition });
|
|
56
|
+
expect(getLocalizedAttributes).toHaveBeenCalledTimes(2);
|
|
57
|
+
expect(find).not.toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("Doesn't migrate if no attribute changed (with i18n)", async () => {
|
|
61
|
+
const find = jest.fn();
|
|
62
|
+
const getLocalizedAttributes = jest.fn(() => ['name']);
|
|
63
|
+
global.strapi = {
|
|
64
|
+
query: () => {
|
|
65
|
+
find;
|
|
66
|
+
},
|
|
67
|
+
plugins: {
|
|
68
|
+
i18n: {
|
|
69
|
+
services: {
|
|
70
|
+
'content-types': {
|
|
71
|
+
isLocalizedContentType: () => true,
|
|
72
|
+
getLocalizedAttributes,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const model = { attributes: { name: {} } };
|
|
80
|
+
const previousDefinition = { attributes: { name: {} } };
|
|
81
|
+
|
|
82
|
+
await after({ model, definition: model, previousDefinition });
|
|
83
|
+
expect(getLocalizedAttributes).toHaveBeenCalledTimes(2);
|
|
84
|
+
expect(find).not.toHaveBeenCalled();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("Doesn't migrate if field become localized", async () => {
|
|
88
|
+
const find = jest.fn();
|
|
89
|
+
const getLocalizedAttributes = jest
|
|
90
|
+
.fn()
|
|
91
|
+
.mockReturnValueOnce(['name'])
|
|
92
|
+
.mockReturnValueOnce([]);
|
|
93
|
+
|
|
94
|
+
global.strapi = {
|
|
95
|
+
query: () => {
|
|
96
|
+
find;
|
|
97
|
+
},
|
|
98
|
+
plugins: {
|
|
99
|
+
i18n: {
|
|
100
|
+
services: {
|
|
101
|
+
'content-types': {
|
|
102
|
+
isLocalizedContentType: () => true,
|
|
103
|
+
getLocalizedAttributes,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const model = { attributes: { name: {} } };
|
|
111
|
+
const previousDefinition = { attributes: { name: {} } };
|
|
112
|
+
|
|
113
|
+
await after({ model, definition: model, previousDefinition });
|
|
114
|
+
expect(getLocalizedAttributes).toHaveBeenCalledTimes(2);
|
|
115
|
+
expect(find).not.toHaveBeenCalled();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("Doesn't migrate if field is deleted", async () => {
|
|
119
|
+
const find = jest.fn();
|
|
120
|
+
const getLocalizedAttributes = jest
|
|
121
|
+
.fn()
|
|
122
|
+
.mockReturnValueOnce([])
|
|
123
|
+
.mockReturnValueOnce(['name']);
|
|
124
|
+
|
|
125
|
+
global.strapi = {
|
|
126
|
+
query: () => {
|
|
127
|
+
find;
|
|
128
|
+
},
|
|
129
|
+
plugins: {
|
|
130
|
+
i18n: {
|
|
131
|
+
services: {
|
|
132
|
+
'content-types': {
|
|
133
|
+
isLocalizedContentType: () => true,
|
|
134
|
+
getLocalizedAttributes,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const model = { attributes: {} };
|
|
142
|
+
const previousDefinition = { attributes: { name: {} } };
|
|
143
|
+
|
|
144
|
+
await after({ model, definition: model, previousDefinition });
|
|
145
|
+
expect(getLocalizedAttributes).toHaveBeenCalledTimes(2);
|
|
146
|
+
expect(find).not.toHaveBeenCalled();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { getDefaultLocale } = require('../utils');
|
|
4
|
+
const { getService } = require('../../../../../utils');
|
|
5
|
+
|
|
6
|
+
const migrateForBookshelf = require('./migrate-for-bookshelf');
|
|
7
|
+
const migrateForMongoose = require('./migrate-for-mongoose');
|
|
8
|
+
|
|
9
|
+
const after = () => {};
|
|
10
|
+
|
|
11
|
+
// Disable i18n on CT -> Delete all entities that are not in the default locale
|
|
12
|
+
const before = async ({ model, definition, previousDefinition, ORM }, context) => {
|
|
13
|
+
const { isLocalizedContentType } = getService('content-types');
|
|
14
|
+
|
|
15
|
+
if (isLocalizedContentType(definition) || !isLocalizedContentType(previousDefinition)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const defaultLocale = await getDefaultLocale(model, ORM);
|
|
20
|
+
|
|
21
|
+
if (model.orm === 'bookshelf') {
|
|
22
|
+
await migrateForBookshelf(
|
|
23
|
+
{ ORM, defaultLocale, definition, previousDefinition, model },
|
|
24
|
+
context
|
|
25
|
+
);
|
|
26
|
+
} else if (model.orm === 'mongoose') {
|
|
27
|
+
await migrateForMongoose({ ORM, defaultLocale, model });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
before,
|
|
33
|
+
after,
|
|
34
|
+
};
|