@localazy/strapi-plugin 0.1.12 → 0.1.13-rc.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/admin/src/index.js +26 -1
- package/admin/src/modules/@common/components/LanguagesSelector/index.js +69 -0
- package/admin/src/modules/@common/locale/en.js +1 -0
- package/admin/src/modules/localazy-download/locale/en.js +3 -0
- package/admin/src/modules/plugin-settings/components/ContentTransferSetup/index.js +1 -2
- package/admin/src/modules/plugin-settings/components/GlobalSettings/index.js +269 -0
- package/admin/src/modules/plugin-settings/locale/en.js +38 -3
- package/admin/src/modules/plugin-settings/services/strapi-users-service.js +26 -0
- package/admin/src/pages/Download/index.js +49 -2
- package/admin/src/pages/PluginSettings/index.js +27 -11
- package/admin/src/utils/createAxiosInstance.js +2 -0
- package/package.json +2 -2
- package/server/bootstrap.js +50 -9
- package/server/controllers/localazy-transfer-controller.js +42 -4
- package/server/controllers/strapi-controller.js +3 -0
- package/server/lifecycles/deep-populate-hook.js +11 -0
- package/server/lifecycles/deprecate-event-entry-in-localazy-hook.js +69 -0
- package/server/lifecycles/upload-event-entry-to-localazy-hook.js +31 -0
- package/server/middlewares/index.js +5 -1
- package/server/middlewares/verify-webhook.js +77 -0
- package/server/routes/index.js +2 -0
- package/server/routes/localazy-public-transfer.js +20 -0
- package/server/routes/strapi.js +8 -0
- package/server/services/helpers/plugin-settings-service-helper.js +52 -0
- package/server/services/localazy-pubapi-service.js +6 -0
- package/server/services/plugin-settings-service.js +7 -1
- package/server/services/strapi-i18n-service.js +1 -1
- package/server/utils/get-picked-flatten-keys-for-hook-entry.js +270 -0
- package/server/utils/request-initiator-helper.js +36 -0
package/admin/src/index.js
CHANGED
|
@@ -12,6 +12,20 @@ const SETTINGS_SETUP = [
|
|
|
12
12
|
intlId: `localazy_settings`,
|
|
13
13
|
sectionDefaultLabel: "Localazy Settings",
|
|
14
14
|
links: [
|
|
15
|
+
{
|
|
16
|
+
linkId: `${pluginId}-settings-global`,
|
|
17
|
+
intlId: `general`,
|
|
18
|
+
defaultLabel: "Global Settings",
|
|
19
|
+
to: `/settings/${pluginId}/global-settings`,
|
|
20
|
+
Component: async () => {
|
|
21
|
+
const component = await import("./pages/PluginSettings");
|
|
22
|
+
|
|
23
|
+
return component;
|
|
24
|
+
},
|
|
25
|
+
permissions: [
|
|
26
|
+
// TODO: only allow access to this page if the user has the right permissions
|
|
27
|
+
],
|
|
28
|
+
},
|
|
15
29
|
{
|
|
16
30
|
linkId: `${pluginId}-settings-content-transfer-setup`,
|
|
17
31
|
intlId: `content_transfer_setup`,
|
|
@@ -61,7 +75,7 @@ export default {
|
|
|
61
75
|
},
|
|
62
76
|
},
|
|
63
77
|
[
|
|
64
|
-
//
|
|
78
|
+
// General
|
|
65
79
|
{
|
|
66
80
|
id: SETTINGS_SETUP[0].links[0].linkId,
|
|
67
81
|
intlLabel: {
|
|
@@ -72,6 +86,17 @@ export default {
|
|
|
72
86
|
Component: SETTINGS_SETUP[0].links[0].Component,
|
|
73
87
|
permissions: SETTINGS_SETUP[0].links[0].permissions,
|
|
74
88
|
},
|
|
89
|
+
// Content Transfer Setup
|
|
90
|
+
{
|
|
91
|
+
id: SETTINGS_SETUP[0].links[1].linkId,
|
|
92
|
+
intlLabel: {
|
|
93
|
+
id: SETTINGS_SETUP[0].links[1].intlId,
|
|
94
|
+
defaultMessage: SETTINGS_SETUP[0].links[1].defaultLabel,
|
|
95
|
+
},
|
|
96
|
+
to: SETTINGS_SETUP[0].links[1].to,
|
|
97
|
+
Component: SETTINGS_SETUP[0].links[1].Component,
|
|
98
|
+
permissions: SETTINGS_SETUP[0].links[1].permissions,
|
|
99
|
+
},
|
|
75
100
|
]
|
|
76
101
|
);
|
|
77
102
|
app.registerPlugin({
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* eslint-disable react/require-default-props */
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import cloneDeep from "lodash-es/cloneDeep";
|
|
6
|
+
import { Select, Option } from '@strapi/design-system/Select';
|
|
7
|
+
|
|
8
|
+
import "../../../../i18n";
|
|
9
|
+
|
|
10
|
+
function LanguagesSelector(props) {
|
|
11
|
+
/**
|
|
12
|
+
* Translation function
|
|
13
|
+
*/
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Selected languages
|
|
18
|
+
*/
|
|
19
|
+
const [selectedLanguages, setSelectedLanguages] = useState([]);
|
|
20
|
+
|
|
21
|
+
const onChange = (values) => {
|
|
22
|
+
setSelectedLanguages(values);
|
|
23
|
+
props.onChange(cloneDeep(values));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const existingPreselectedLanguages =
|
|
28
|
+
props.projectLanguages
|
|
29
|
+
.filter(lang => props.preselectedLanguages.includes(lang.code))
|
|
30
|
+
.map(lang => lang.code);
|
|
31
|
+
|
|
32
|
+
setSelectedLanguages(existingPreselectedLanguages);
|
|
33
|
+
}, [props.projectLanguages, props.preselectedLanguages]);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Select
|
|
37
|
+
label={props.label || t("common.select_languages")}
|
|
38
|
+
hint={props.hint || t("common.select_languages")}
|
|
39
|
+
clearLabel={t("plugin_settings.clear")}
|
|
40
|
+
placeholder={props.placeholder || t("common.select_languages")}
|
|
41
|
+
onClear={() => setSelectedLanguages([])}
|
|
42
|
+
value={selectedLanguages || []}
|
|
43
|
+
onChange={(values) => onChange(values)}
|
|
44
|
+
multi
|
|
45
|
+
withTags
|
|
46
|
+
>
|
|
47
|
+
{props.projectLanguages.map(lang => (
|
|
48
|
+
<Option
|
|
49
|
+
key={lang.id}
|
|
50
|
+
value={lang.code}
|
|
51
|
+
>
|
|
52
|
+
{`${lang.name} (${lang.code})`}
|
|
53
|
+
</Option>
|
|
54
|
+
))}
|
|
55
|
+
</Select>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
LanguagesSelector.propTypes = {
|
|
60
|
+
label: PropTypes.string,
|
|
61
|
+
hint: PropTypes.string,
|
|
62
|
+
placeholder: PropTypes.string,
|
|
63
|
+
projectLanguages: PropTypes.array.isRequired,
|
|
64
|
+
preselectedLanguages: PropTypes.array.isRequired,
|
|
65
|
+
onChange: PropTypes.func.isRequired,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
export default LanguagesSelector;
|
|
@@ -17,4 +17,7 @@ export default {
|
|
|
17
17
|
make_sure_note_a: "Make sure the translations are approved in Localazy, otherwise they ",
|
|
18
18
|
make_sure_note_b: "will not ",
|
|
19
19
|
make_sure_note_c: "be downloaded to Strapi.",
|
|
20
|
+
ui_languages: "Select languages to download",
|
|
21
|
+
ui_languages_info: "Select translations of which languages wil be downloaded from Localazy",
|
|
22
|
+
ui_languages_placeholder: "Download all languages",
|
|
20
23
|
};
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { useTranslation } from "react-i18next";
|
|
3
|
+
import cloneDeep from "lodash-es/cloneDeep";
|
|
4
|
+
import { HeaderLayout } from "@strapi/design-system/Layout";
|
|
5
|
+
import Check from "@strapi/icons/Check";
|
|
6
|
+
import { Box } from "@strapi/design-system/Box";
|
|
7
|
+
import { Button } from "@strapi/design-system/Button";
|
|
8
|
+
import { ToggleInput } from '@strapi/design-system/ToggleInput';
|
|
9
|
+
import { Select, Option } from '@strapi/design-system/Select';
|
|
10
|
+
import { Alert } from "@strapi/design-system/Alert";
|
|
11
|
+
import { Redirect } from "react-router-dom";
|
|
12
|
+
import { Flex } from "@strapi/design-system/Flex";
|
|
13
|
+
import { Divider } from "@strapi/design-system/Divider";
|
|
14
|
+
import { Typography } from "@strapi/design-system/Typography";
|
|
15
|
+
import isEqual from "lodash-es/isEqual";
|
|
16
|
+
import set from "lodash-es/set";
|
|
17
|
+
import LanguagesSelector from "../../../@common/components/LanguagesSelector";
|
|
18
|
+
import PluginSettingsService from "../../services/plugin-settings-service";
|
|
19
|
+
import StrapiUsersService from "../../services/strapi-users-service";
|
|
20
|
+
import { getLocalazyIdentity } from "../../../../state/localazy-identity";
|
|
21
|
+
import ProjectService from "../../../@common/services/project-service";
|
|
22
|
+
import pluginId from "../../../../pluginId";
|
|
23
|
+
|
|
24
|
+
import "../../../../i18n";
|
|
25
|
+
|
|
26
|
+
function GlobalSettings() {
|
|
27
|
+
/**
|
|
28
|
+
* Translation function
|
|
29
|
+
*/
|
|
30
|
+
const { t } = useTranslation();
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Is user logged in
|
|
34
|
+
*/
|
|
35
|
+
const [localazyIdentity] = getLocalazyIdentity();
|
|
36
|
+
const hasLocalazyIdentity = () => !!localazyIdentity.accessToken;
|
|
37
|
+
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Project Languages without default language
|
|
41
|
+
*/
|
|
42
|
+
const [projectLanguages, setProjectLanguages] = useState([]);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Component state
|
|
46
|
+
*/
|
|
47
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
48
|
+
const [showSavedAlert, setShowSavedAlert] = useState(false);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Admin Panel Users
|
|
52
|
+
*/
|
|
53
|
+
const [users, setUsers] = useState([]);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Global Settings form model
|
|
57
|
+
*/
|
|
58
|
+
const [originalFormModel, setOriginalFormModel] = useState([]);
|
|
59
|
+
const [formModel, setFormModel] = useState([]);
|
|
60
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
61
|
+
|
|
62
|
+
const onCancelClick = () => {
|
|
63
|
+
setFormModel(cloneDeep(originalFormModel));
|
|
64
|
+
setHasUnsavedChanges(false);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Save the Global Settings
|
|
69
|
+
*/
|
|
70
|
+
const saveGlobalSettings = async (data) => {
|
|
71
|
+
try {
|
|
72
|
+
await PluginSettingsService.updatePluginSettings(data);
|
|
73
|
+
setOriginalFormModel(cloneDeep(data));
|
|
74
|
+
setHasUnsavedChanges(false);
|
|
75
|
+
setShowSavedAlert(true);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
throw e.data;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Patch Form Model
|
|
83
|
+
*/
|
|
84
|
+
const patchFormModel = (key, value) => {
|
|
85
|
+
const newFormModel = cloneDeep(formModel);
|
|
86
|
+
set(newFormModel, key, value);
|
|
87
|
+
setFormModel(newFormModel);
|
|
88
|
+
setHasUnsavedChanges(!isEqual(originalFormModel, newFormModel));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
async function fetchData() {
|
|
93
|
+
setIsLoading(true);
|
|
94
|
+
setIsLoggedIn(hasLocalazyIdentity());
|
|
95
|
+
|
|
96
|
+
if (!hasLocalazyIdentity()) {
|
|
97
|
+
setIsLoading(false);
|
|
98
|
+
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const project = await ProjectService.getConnectedProject();
|
|
103
|
+
const projectLanguagesWithoutDefaultLanguage =
|
|
104
|
+
project?.languages?.filter(language => language.id !== project.sourceLanguage) || [];
|
|
105
|
+
setProjectLanguages(projectLanguagesWithoutDefaultLanguage);
|
|
106
|
+
|
|
107
|
+
const globalSettings = await PluginSettingsService.getPluginSettings();
|
|
108
|
+
|
|
109
|
+
setFormModel(globalSettings);
|
|
110
|
+
setOriginalFormModel(cloneDeep(globalSettings));
|
|
111
|
+
|
|
112
|
+
const users = await StrapiUsersService.getAdminPanelUsers();
|
|
113
|
+
setUsers(users);
|
|
114
|
+
|
|
115
|
+
setIsLoading(false);
|
|
116
|
+
}
|
|
117
|
+
fetchData();
|
|
118
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
119
|
+
}, []);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<>
|
|
123
|
+
{!isLoading && !isLoggedIn && <Redirect to={`${process.env.ADMIN_PATH}plugins/${pluginId}/login`} />}
|
|
124
|
+
|
|
125
|
+
<HeaderLayout
|
|
126
|
+
title={t("plugin_settings.global_settings")}
|
|
127
|
+
subtitle={t("plugin_settings.global_settings_description")}
|
|
128
|
+
primaryAction={
|
|
129
|
+
<Flex gap={2}>
|
|
130
|
+
<Button
|
|
131
|
+
variant="secondary"
|
|
132
|
+
disabled={!hasUnsavedChanges}
|
|
133
|
+
onClick={onCancelClick}
|
|
134
|
+
>
|
|
135
|
+
{t("plugin_settings.cancel")}
|
|
136
|
+
</Button>
|
|
137
|
+
<Button
|
|
138
|
+
startIcon={<Check />}
|
|
139
|
+
disabled={!hasUnsavedChanges}
|
|
140
|
+
onClick={() => { saveGlobalSettings(formModel) }}
|
|
141
|
+
>
|
|
142
|
+
{t("plugin_settings.save")}
|
|
143
|
+
</Button>
|
|
144
|
+
</Flex>
|
|
145
|
+
}
|
|
146
|
+
as="h2"
|
|
147
|
+
/>
|
|
148
|
+
<Box marginRight={10} marginLeft={10}>
|
|
149
|
+
{!isLoading && showSavedAlert && (
|
|
150
|
+
<Box marginBottom={8}>
|
|
151
|
+
<Alert
|
|
152
|
+
onClose={() => showSavedAlert(false)}
|
|
153
|
+
closeLabel={t("plugin_settings.close")}
|
|
154
|
+
title={t("plugin_settings.content_transfer_setup_saved")}
|
|
155
|
+
variant="success"
|
|
156
|
+
>
|
|
157
|
+
{t("plugin_settings.global_settings_saved_successfully")}
|
|
158
|
+
</Alert>
|
|
159
|
+
</Box>
|
|
160
|
+
)}
|
|
161
|
+
{!isLoading && (
|
|
162
|
+
<Box
|
|
163
|
+
background="neutral0"
|
|
164
|
+
padding={7}
|
|
165
|
+
paddingTop={6}
|
|
166
|
+
shadow="tableShadow"
|
|
167
|
+
hasRadius
|
|
168
|
+
>
|
|
169
|
+
{/* Upload Settings */}
|
|
170
|
+
<Typography
|
|
171
|
+
variant="delta"
|
|
172
|
+
textColor="neutral800"
|
|
173
|
+
>
|
|
174
|
+
{t("plugin_settings.upload_settings")}
|
|
175
|
+
</Typography>
|
|
176
|
+
{/* Allow automated upload to Localazy */}
|
|
177
|
+
<br /><br />
|
|
178
|
+
<ToggleInput
|
|
179
|
+
label={t("plugin_settings.allow_automated_upload_to_localazy")}
|
|
180
|
+
hint={t("plugin_settings.allow_automated_upload_to_localazy_info")}
|
|
181
|
+
offLabel={t("plugin_settings.off")}
|
|
182
|
+
onLabel={t("plugin_settings.on")}
|
|
183
|
+
checked={typeof formModel?.upload?.allowAutomated === "boolean" ? formModel.upload.allowAutomated : false}
|
|
184
|
+
onChange={e => patchFormModel("upload.allowAutomated", e.target.checked)} />
|
|
185
|
+
{/* Automated upload triggers */}
|
|
186
|
+
<br /><br />
|
|
187
|
+
<Select
|
|
188
|
+
label={t("plugin_settings.automated_upload_triggers")}
|
|
189
|
+
hint={t("plugin_settings.automated_upload_triggers_info")}
|
|
190
|
+
clearLabel={t("plugin_settings.clear")}
|
|
191
|
+
placeholder={t("plugin_settings.automated_upload_triggers_placeholder")}
|
|
192
|
+
onClear={() => patchFormModel("upload.automatedTriggers", [])}
|
|
193
|
+
value={formModel?.upload?.automatedTriggers || []}
|
|
194
|
+
onChange={(values) => patchFormModel("upload.automatedTriggers", values)}
|
|
195
|
+
disabled={typeof formModel?.upload?.allowAutomated === "boolean" ? !formModel.upload.allowAutomated : true}
|
|
196
|
+
multi
|
|
197
|
+
withTags
|
|
198
|
+
>
|
|
199
|
+
<Option value="created">{t("plugin_settings.creating_new_data_entry")}</Option>
|
|
200
|
+
<Option value="updated">{t("plugin_settings.editing_data_entry")}</Option>
|
|
201
|
+
</Select>
|
|
202
|
+
{/* Deprecate source keys on deletion */}
|
|
203
|
+
<br /><br />
|
|
204
|
+
<ToggleInput
|
|
205
|
+
label={t("plugin_settings.deprecate_source_keys_on_delete")}
|
|
206
|
+
hint={t("plugin_settings.deprecate_source_keys_on_delete_info")}
|
|
207
|
+
offLabel={t("plugin_settings.off")}
|
|
208
|
+
onLabel={t("plugin_settings.on")}
|
|
209
|
+
checked={typeof formModel?.upload?.allowDeprecate === "boolean" ? formModel.upload.allowDeprecate : false}
|
|
210
|
+
onChange={e => patchFormModel("upload.allowDeprecate", e.target.checked)} />
|
|
211
|
+
<br /><br />
|
|
212
|
+
<Divider />
|
|
213
|
+
{/* Download Settings */}
|
|
214
|
+
<br /><br />
|
|
215
|
+
<Typography
|
|
216
|
+
variant="delta"
|
|
217
|
+
textColor="neutral800"
|
|
218
|
+
>
|
|
219
|
+
{t("plugin_settings.download_settings")}
|
|
220
|
+
</Typography>
|
|
221
|
+
{/* Processing of download webhook */}
|
|
222
|
+
<br /><br />
|
|
223
|
+
<ToggleInput
|
|
224
|
+
label={t("plugin_settings.processing_of_download_webhook")}
|
|
225
|
+
hint={t("plugin_settings.processing_of_download_webhook_info")}
|
|
226
|
+
offLabel={t("plugin_settings.off")}
|
|
227
|
+
onLabel={t("plugin_settings.on")}
|
|
228
|
+
// eslint-disable-next-line max-len
|
|
229
|
+
checked={typeof formModel?.download?.processDownloadWebhook === "boolean" ? formModel.download.processDownloadWebhook : true}
|
|
230
|
+
onChange={e => patchFormModel("download.processDownloadWebhook", e.target.checked)} />
|
|
231
|
+
{/* Webhook actions author */}
|
|
232
|
+
<br /><br />
|
|
233
|
+
<Select
|
|
234
|
+
label={t("plugin_settings.webhook_author")}
|
|
235
|
+
hint={t("plugin_settings.webhook_author_info")}
|
|
236
|
+
clearLabel={t("plugin_settings.clear")}
|
|
237
|
+
placeholder={t("plugin_settings.webhook_author_placeholder")}
|
|
238
|
+
onClear={() => patchFormModel("download.webhookAuthorId", null)}
|
|
239
|
+
value={formModel?.download?.webhookAuthorId || null}
|
|
240
|
+
onChange={(value) => patchFormModel("download.webhookAuthorId", value)}
|
|
241
|
+
>
|
|
242
|
+
{users.map(user => (
|
|
243
|
+
<Option
|
|
244
|
+
key={user.id}
|
|
245
|
+
value={user.id}
|
|
246
|
+
>
|
|
247
|
+
{`${user.firstname} ${user.lastname} (${user.email})`}
|
|
248
|
+
</Option>
|
|
249
|
+
))}
|
|
250
|
+
</Select>
|
|
251
|
+
{/* Webhook languages selector */}
|
|
252
|
+
<br /><br />
|
|
253
|
+
<LanguagesSelector
|
|
254
|
+
preselectedLanguages={formModel?.download?.webhookLanguages || []}
|
|
255
|
+
projectLanguages={projectLanguages}
|
|
256
|
+
onChange={(languages) => patchFormModel("download.webhookLanguages", languages)}
|
|
257
|
+
label={t("plugin_settings.webhook_languages")}
|
|
258
|
+
hint={t("plugin_settings.webhook_languages_info")}
|
|
259
|
+
placeholder={t("plugin_settings.webhook_languages_placeholder")}
|
|
260
|
+
/>
|
|
261
|
+
|
|
262
|
+
</Box>
|
|
263
|
+
)}
|
|
264
|
+
</Box>
|
|
265
|
+
</>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export default GlobalSettings;
|
|
@@ -1,10 +1,43 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
content_transfer_setup: "Content Transfer Setup",
|
|
3
|
-
content_transfer_setup_description:
|
|
4
|
-
"Select which content type fields will be uploaded to Localazy",
|
|
5
2
|
save: "Save",
|
|
6
3
|
cancel: "Cancel",
|
|
7
4
|
close: "Close",
|
|
5
|
+
off: "Off",
|
|
6
|
+
on: "On",
|
|
7
|
+
clear: "Clear",
|
|
8
|
+
|
|
9
|
+
/* Global Settings */
|
|
10
|
+
global_settings: "Global Settings",
|
|
11
|
+
global_settings_description: "Setup the Localazy plugin upload/download options.",
|
|
12
|
+
|
|
13
|
+
upload_settings: "Upload settings",
|
|
14
|
+
allow_automated_upload_to_localazy: "Allow automated upload to Localazy",
|
|
15
|
+
allow_automated_upload_to_localazy_info: "Content will be automatically uploaded to Localazy after its creation or update.",
|
|
16
|
+
automated_upload_triggers: "Automated upload triggers",
|
|
17
|
+
automated_upload_triggers_placeholder: "Select triggers",
|
|
18
|
+
creating_new_data_entry: "Creating new data entry",
|
|
19
|
+
editing_data_entry: "Editing data entry",
|
|
20
|
+
automated_upload_triggers_info: "Select actions that will trigger an upload of Strapi data to Localazy.",
|
|
21
|
+
|
|
22
|
+
deprecate_source_keys_on_delete: "Deprecate source keys on deletion",
|
|
23
|
+
deprecate_source_keys_on_delete_info: "Source keys will be automatically set as deprecated if an entry in source language is deleted in Strapi",
|
|
24
|
+
|
|
25
|
+
download_settings: "Download settings",
|
|
26
|
+
processing_of_download_webhook: "Processing of download webhook",
|
|
27
|
+
processing_of_download_webhook_info: "You can temporarily disable the webhook processing and the information will be scrapped.",
|
|
28
|
+
webhook_author: "Webhook actions author",
|
|
29
|
+
webhook_author_info: "Select the author signed under the webhook actions (required by Strapi for certain actions, e.g. creating a locale).",
|
|
30
|
+
webhook_author_placeholder: "Select webhook actions author",
|
|
31
|
+
webhook_languages: "Allowed webhook languages",
|
|
32
|
+
webhook_languages_info: "Select the languages from Localazy. Translations of selected languages would be downloaded. If no language is selected, all translations will be downloaded.",
|
|
33
|
+
webhook_languages_placeholder: "Download all languages",
|
|
34
|
+
|
|
35
|
+
global_settings_saved_successfully: "Global Settings were saved",
|
|
36
|
+
|
|
37
|
+
/* Content Transfer Setup */
|
|
38
|
+
content_transfer_setup: "Content Transfer Setup",
|
|
39
|
+
content_transfer_setup_description:
|
|
40
|
+
"Select which content type fields will be uploaded to Localazy",
|
|
8
41
|
|
|
9
42
|
content_transfer_setup_saved: "Content Transfer Setup saved",
|
|
10
43
|
content_transfer_setup_saved_successfully:
|
|
@@ -23,4 +56,6 @@ export default {
|
|
|
23
56
|
step_3: "Check <1>Enable localization for this Content-Type</1>",
|
|
24
57
|
step_4: "Repeat for all Content-Types you want to translate",
|
|
25
58
|
step_5: "Return to this screen and finish the transfer setup",
|
|
59
|
+
|
|
60
|
+
general: "General",
|
|
26
61
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* eslint-disable no-useless-catch */
|
|
2
|
+
import createAxiosInstance from "../../../utils/createAxiosInstance";
|
|
3
|
+
|
|
4
|
+
const axiosInstance = createAxiosInstance("/admin/users");
|
|
5
|
+
|
|
6
|
+
export default class PluginSettingsService {
|
|
7
|
+
static async getAdminPanelUsers() {
|
|
8
|
+
try {
|
|
9
|
+
// TODO: update to return all users; not just the first 100
|
|
10
|
+
const result = await axiosInstance.get(
|
|
11
|
+
"",
|
|
12
|
+
{
|
|
13
|
+
params: {
|
|
14
|
+
"pageSize": 100,
|
|
15
|
+
"page": 1,
|
|
16
|
+
"sort": "firstname"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return result.data.data.results;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
throw e;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
+
import cloneDeep from "lodash-es/cloneDeep";
|
|
4
|
+
import set from "lodash-es/set";
|
|
3
5
|
import { HeaderLayout } from "@strapi/design-system/Layout";
|
|
4
6
|
import { Button } from "@strapi/design-system/Button";
|
|
5
7
|
import DownloadIcon from "@strapi/icons/Download";
|
|
@@ -20,6 +22,8 @@ import { getLocalazyIdentity } from "../../state/localazy-identity";
|
|
|
20
22
|
import ProductAnalyticsService from "../../modules/@common/services/product-analytics-service";
|
|
21
23
|
import PluginSettingsService from "../../modules/plugin-settings/services/plugin-settings-service";
|
|
22
24
|
import { PLUGIN_ROUTES } from "../../modules/@common/utils/redirect-to-plugin-route";
|
|
25
|
+
import LanguagesSelector from "../../modules/@common/components/LanguagesSelector";
|
|
26
|
+
import ProjectService from "../../modules/@common/services/project-service";
|
|
23
27
|
|
|
24
28
|
function Download(props) {
|
|
25
29
|
const { t } = useTranslation();
|
|
@@ -36,13 +40,36 @@ function Download(props) {
|
|
|
36
40
|
const [isDownloading, setIsDownloading] = useState(false);
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
* Localazy identity / access token
|
|
44
|
+
*/
|
|
41
45
|
const [localazyIdentity] = getLocalazyIdentity();
|
|
42
46
|
const onTranslateInLocalazyClick = () => {
|
|
43
47
|
window.open(localazyIdentity.project.url, "_blank");
|
|
44
48
|
}
|
|
45
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Project Languages without default language
|
|
52
|
+
*/
|
|
53
|
+
const [projectLanguages, setProjectLanguages] = useState([]);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Global Settings form model
|
|
57
|
+
*/
|
|
58
|
+
const [formModel, setFormModel] = useState([]);
|
|
59
|
+
|
|
60
|
+
const onDownloadLanguagesChange = (languages) => {
|
|
61
|
+
const newFormModel = cloneDeep(formModel);
|
|
62
|
+
set(newFormModel, 'download.uiLanguages', languages);
|
|
63
|
+
setFormModel(newFormModel);
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
PluginSettingsService.updatePluginSettings(newFormModel);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.error(e.message);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
|
|
46
73
|
const onDownloadClick = async () => {
|
|
47
74
|
setIsDownloading(true);
|
|
48
75
|
const result = await LocalazyDownloadService.download();
|
|
@@ -71,6 +98,14 @@ function Download(props) {
|
|
|
71
98
|
setModelChanged(await hasModelChanged());
|
|
72
99
|
setLocalesIncompatible(!(await areLocalesCompatible()));
|
|
73
100
|
|
|
101
|
+
const project = await ProjectService.getConnectedProject();
|
|
102
|
+
const projectLanguagesWithoutDefaultLanguage =
|
|
103
|
+
project?.languages?.filter(language => language.id !== project.sourceLanguage) || [];
|
|
104
|
+
setProjectLanguages(projectLanguagesWithoutDefaultLanguage);
|
|
105
|
+
|
|
106
|
+
const globalSettings = await PluginSettingsService.getPluginSettings();
|
|
107
|
+
setFormModel(globalSettings);
|
|
108
|
+
|
|
74
109
|
PluginSettingsService.updatePluginSettings({ defaultRoute: PLUGIN_ROUTES.DOWNLOAD });
|
|
75
110
|
|
|
76
111
|
setIsLoading(false);
|
|
@@ -124,6 +159,18 @@ function Download(props) {
|
|
|
124
159
|
<Typography variant="omega">
|
|
125
160
|
{t("download.make_sure_note_c")}
|
|
126
161
|
</Typography>
|
|
162
|
+
<Box
|
|
163
|
+
paddingTop={6}
|
|
164
|
+
>
|
|
165
|
+
<LanguagesSelector
|
|
166
|
+
preselectedLanguages={formModel?.download?.uiLanguages || []}
|
|
167
|
+
projectLanguages={projectLanguages}
|
|
168
|
+
onChange={(languages) => onDownloadLanguagesChange(languages)}
|
|
169
|
+
label={t("download.ui_languages")}
|
|
170
|
+
hint={t("download.ui_languages_info")}
|
|
171
|
+
placeholder={t("download.ui_languages_placeholder")}
|
|
172
|
+
/>
|
|
173
|
+
</Box>
|
|
127
174
|
<Box
|
|
128
175
|
paddingTop={4}
|
|
129
176
|
paddingBottom={4}
|
|
@@ -3,12 +3,22 @@ import { Box } from "@strapi/design-system/Box";
|
|
|
3
3
|
import { Switch, Route } from "react-router-dom";
|
|
4
4
|
import { ThemeProvider } from "@strapi/design-system/ThemeProvider";
|
|
5
5
|
import { lightTheme } from "@strapi/design-system/themes";
|
|
6
|
+
import { useTranslation } from "react-i18next";
|
|
6
7
|
import pluginId from "../../pluginId";
|
|
7
8
|
import ContentTransferSetup from "../../modules/plugin-settings/components/ContentTransferSetup";
|
|
9
|
+
import GlobalSettings from "../../modules/plugin-settings/components/GlobalSettings";
|
|
8
10
|
import LocalazyUserService from "../../modules/user/services/localazy-user-service";
|
|
9
11
|
import { setLocalazyIdentity } from "../../state/localazy-identity";
|
|
12
|
+
import Loader from "../../modules/@common/components/PluginPageLoader";
|
|
13
|
+
|
|
14
|
+
import "../../i18n";
|
|
10
15
|
|
|
11
16
|
function PluginSettings() {
|
|
17
|
+
/**
|
|
18
|
+
* Translation function
|
|
19
|
+
*/
|
|
20
|
+
const { t } = useTranslation();
|
|
21
|
+
|
|
12
22
|
const [isLoading, setIsLoading] = useState(true);
|
|
13
23
|
|
|
14
24
|
useEffect(() => {
|
|
@@ -25,17 +35,23 @@ function PluginSettings() {
|
|
|
25
35
|
}, []);
|
|
26
36
|
|
|
27
37
|
return (
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
<>
|
|
39
|
+
{isLoading && <Loader>{t("common.loading_content")}</Loader>}
|
|
40
|
+
{!isLoading && (
|
|
41
|
+
<ThemeProvider theme={lightTheme}>
|
|
42
|
+
<Box padding={8}>
|
|
43
|
+
<Switch>
|
|
44
|
+
<Route path={`/settings/${pluginId}/content-transfer-setup`} exact>
|
|
45
|
+
<ContentTransferSetup />
|
|
46
|
+
</Route>
|
|
47
|
+
<Route path={`/settings/${pluginId}/global-settings`} exact>
|
|
48
|
+
<GlobalSettings />
|
|
49
|
+
</Route>
|
|
50
|
+
</Switch>
|
|
51
|
+
</Box>
|
|
52
|
+
</ThemeProvider>
|
|
53
|
+
)}
|
|
54
|
+
</>
|
|
39
55
|
);
|
|
40
56
|
}
|
|
41
57
|
|
|
@@ -23,6 +23,8 @@ const createAxiosInstance = (baseUrl = null) => {
|
|
|
23
23
|
Authorization: `Bearer ${auth.getToken()}`,
|
|
24
24
|
Accept: "application/json",
|
|
25
25
|
"Content-Type": "application/json",
|
|
26
|
+
// Add this header to identify the request as initiated by the plugin
|
|
27
|
+
"X-Localazy-Initiated-By": "strapi-plugin-localazy",
|
|
26
28
|
};
|
|
27
29
|
|
|
28
30
|
return config;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localazy/strapi-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13-rc.0",
|
|
4
4
|
"description": "The official Strapi Plugin by Localazy.",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"name": "localazy",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@david-vaclavek/deep-keys": "^0.5.0",
|
|
31
|
-
"@localazy/ts-api": "^1.0.
|
|
31
|
+
"@localazy/ts-api": "^1.0.12",
|
|
32
32
|
"i18next": "^21.6.13",
|
|
33
33
|
"isomorphic-fetch": "^3.0.0",
|
|
34
34
|
"js-cookie": "^3.0.1",
|