@localazy/strapi-plugin 1.4.0 → 1.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.
Files changed (64) hide show
  1. package/README.md +30 -0
  2. package/dist/_chunks/{FetchIdentity-BpFOT163.js → FetchIdentity-D89DgpbN.js} +24 -32
  3. package/dist/_chunks/{FetchIdentity-Cor4ooZv.mjs → FetchIdentity-DkHj24S5.mjs} +36 -44
  4. package/dist/_chunks/{LanguagesSelector-CC1ecWnc.js → LanguagesSelector-638KGQYw.js} +3 -2
  5. package/dist/_chunks/{LanguagesSelector-BbrMKwQY.mjs → LanguagesSelector-CR_7I2R1.mjs} +4 -3
  6. package/dist/_chunks/{LocalazyApp-D0JGRQeF.js → LocalazyApp-Benn8pkA.js} +75 -39
  7. package/dist/_chunks/{LocalazyApp-DpFR8p10.mjs → LocalazyApp-DM3zg5E4.mjs} +70 -34
  8. package/dist/_chunks/{LocalazyContentTransferSetup-Dael1Q4w.mjs → LocalazyContentTransferSetup-BA_rtSBM.mjs} +58 -22
  9. package/dist/_chunks/{LocalazyContentTransferSetup-Cr7D-BUi.js → LocalazyContentTransferSetup-CWchyGdD.js} +62 -26
  10. package/dist/_chunks/{LocalazyGlobalSettings-D3nG4zl7.js → LocalazyGlobalSettings-88jqQbD4.js} +56 -25
  11. package/dist/_chunks/{LocalazyGlobalSettings-DzBI5M-K.mjs → LocalazyGlobalSettings-BWF0s9tM.mjs} +56 -25
  12. package/dist/_chunks/{LocalazyPanel-W6j8N1WC.mjs → LocalazyPanel-4HRa1jNl.mjs} +12 -6
  13. package/dist/_chunks/{LocalazyPanel-KQPLxTDz.js → LocalazyPanel-D0w9JSIS.js} +14 -8
  14. package/dist/_chunks/{LocalazyStatusColumn-B7Q_U2Lu.js → LocalazyStatusColumn-BR3f8rKp.js} +10 -4
  15. package/dist/_chunks/{LocalazyStatusColumn-CrhsCjgp.mjs → LocalazyStatusColumn-D4-rZZUK.mjs} +10 -4
  16. package/dist/_chunks/{RequireLocalazyAuth-D3GeZPDh.js → RequireLocalazyAuth-C442750b.js} +30 -3
  17. package/dist/_chunks/{RequireLocalazyAuth-BwoGRfJq.mjs → RequireLocalazyAuth-DbNjEOq-.mjs} +32 -5
  18. package/dist/_chunks/{entry-exclusion-service-D7gaRHPE.mjs → entry-exclusion-service-Bd3mUDde.mjs} +1 -1
  19. package/dist/_chunks/{entry-exclusion-service-Bwruvyce.js → entry-exclusion-service-CAIcybXX.js} +1 -1
  20. package/dist/_chunks/{has-model-changed-Dp2Vkwjw.mjs → has-model-changed-BNs3d9q5.mjs} +4 -3
  21. package/dist/_chunks/{has-model-changed-5IV9WI_-.js → has-model-changed-BrDeyHFt.js} +4 -3
  22. package/dist/_chunks/{i18n-o24yXf-j.mjs → i18n-76ox2llT.mjs} +4 -1
  23. package/dist/_chunks/{i18n-C_5tJvr0.js → i18n-C5ucROBh.js} +4 -1
  24. package/dist/_chunks/{index-C9hN3KbD.mjs → index-C9Px4v-A.mjs} +34 -12
  25. package/dist/_chunks/{index-jAg6jjCu.js → index-DwFm1TyM.js} +33 -11
  26. package/dist/_chunks/{index-BjL9XpRh.mjs → index-shryj0jT.mjs} +16 -16
  27. package/dist/_chunks/{strapi-api-base-IG3Pe5Sg.js → strapi-api-base-BWZYbxxj.js} +1 -1
  28. package/dist/_chunks/{strapi-api-base-CAzGcwF6.mjs → strapi-api-base-lPo7wq6I.mjs} +1 -1
  29. package/dist/admin/index.js +1 -1
  30. package/dist/admin/index.mjs +1 -1
  31. package/dist/admin/src/components/LocalazyStatusColumn.d.ts +1 -1
  32. package/dist/admin/src/constants/permissions.d.ts +29 -0
  33. package/dist/admin/src/modules/@common/components/LanguagesSelector.d.ts +1 -0
  34. package/dist/admin/src/modules/@common/models/supported-content-type-fields.d.ts +1 -1
  35. package/dist/admin/src/modules/login/locale/en.d.ts +1 -0
  36. package/dist/admin/src/modules/plugin-settings/components/Tree.d.ts +1 -0
  37. package/dist/admin/src/modules/plugin-settings/components/TreeItem.d.ts +1 -0
  38. package/dist/admin/src/modules/plugin-settings/components/WebhookSetup.d.ts +4 -1
  39. package/dist/admin/src/modules/plugin-settings/locale/en.d.ts +2 -0
  40. package/dist/admin/src/modules/plugin-settings/services/plugin-settings-service.d.ts +4 -0
  41. package/dist/server/index.js +422 -213
  42. package/dist/server/index.mjs +422 -213
  43. package/dist/server/src/bootstrap.d.ts +1 -1
  44. package/dist/server/src/constants/permissions.d.ts +38 -0
  45. package/dist/server/src/controllers/index.d.ts +1 -0
  46. package/dist/server/src/controllers/plugin-settings-controller.d.ts +1 -0
  47. package/dist/server/src/core/index.d.ts +3 -1
  48. package/dist/server/src/index.d.ts +57 -8
  49. package/dist/server/src/routes/activity-logs-routes.d.ts +20 -3
  50. package/dist/server/src/routes/debug-bundle-routes.d.ts +6 -1
  51. package/dist/server/src/routes/entry-exclusion-routes.d.ts +20 -3
  52. package/dist/server/src/routes/index.d.ts +52 -6
  53. package/dist/server/src/routes/localazy-auth-routes.d.ts +6 -1
  54. package/dist/server/src/routes/localazy-project-routes.d.ts +20 -3
  55. package/dist/server/src/routes/localazy-transfer-routes.d.ts +6 -1
  56. package/dist/server/src/routes/localazy-user-routes.d.ts +20 -3
  57. package/dist/server/src/routes/plugin-settings-routes.d.ts +20 -3
  58. package/dist/server/src/routes/strapi-routes.d.ts +20 -3
  59. package/dist/server/src/services/index.d.ts +3 -1
  60. package/dist/server/src/services/localazy-transfer-download-service.d.ts +2 -1
  61. package/dist/server/src/services/plugin-settings-service.d.ts +4 -0
  62. package/dist/server/src/utils/blocks-to-translatable.d.ts +34 -0
  63. package/dist/server/src/utils/model-utils.d.ts +2 -1
  64. package/package.json +1 -1
package/README.md CHANGED
@@ -71,6 +71,36 @@ localazy: {
71
71
  },
72
72
  ```
73
73
 
74
+ ## 🔐 Access control (RBAC)
75
+
76
+ The plugin registers four Strapi permission actions, all visible under
77
+ **Settings → Administration Panel → Roles → Plugins → Localazy** (grouped
78
+ by `General` and `Settings` sub-categories):
79
+
80
+ | Action | Unlocks |
81
+ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
82
+ | `Localazy → Read` (`plugin::localazy.read`) | Localazy menu link, Overview, Activity Logs (list + detail), Content-Manager side panel & Localazy status column, read endpoints (identity, project, models, plugin settings, **content-transfer-setup**, sync cursor, activity logs, troubleshooting bundle, entry-exclusion state). |
83
+ | `Localazy → Transfer` (`plugin::localazy.transfer`) | Upload, Download, Entry Exclusion mutations (incl. Content-Manager bulk actions), Activity Log session clearing. Reading the content-transfer-setup is bundled under `Read` above so transfer-only roles can still open the Upload/Download pages. |
84
+ | `Localazy → Settings → Read` (`plugin::localazy.settings.read`) | Adds the Localazy Settings menu links (Global Settings, Content Transfer Setup) to the admin sidebar. Form controls render read-only — Save, Cancel, the Content Transfer Setup tree, and the webhook setup buttons are disabled without `Settings → Update`. |
85
+ | `Localazy → Settings → Update` (`plugin::localazy.settings.update`) | **Connecting / disconnecting the Localazy account**, webhook setup, updating Content Transfer Setup, and updating Global Settings. |
86
+
87
+ > **Note:** the **Webhook author** picker in _Localazy Settings → Global
88
+ > Settings_ populates from Strapi's core `/admin/users` endpoint, which is
89
+ > gated by Strapi's own `admin::users.read` permission — independent of the
90
+ > plugin's actions. A role with only the plugin's `Settings → Read` will
91
+ > still see the rest of the page; the author dropdown silently falls back
92
+ > to an empty list.
93
+
94
+ Server is the enforcement perimeter — all admin-typed plugin routes are
95
+ gated by `admin::hasPermissions`, so UI gates are convenience only.
96
+
97
+ ### Upgrade note
98
+
99
+ Strapi grants new actions to the **Super Admin** role automatically. **Other
100
+ roles (Editor, Author, custom roles) keep no Localazy access until an admin
101
+ re-grants the actions** under _Settings → Administration Panel → Roles →
102
+ Plugins → Localazy_. Plan this step when upgrading from `<= 1.4.x`.
103
+
74
104
  ## 🛟 Support
75
105
 
76
106
  If you encounter any issues or have questions, feel free to contact us through whichever channel suits you best:
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
- const strapiApiBase = require("./strapi-api-base-IG3Pe5Sg.js");
2
+ const strapiApiBase = require("./strapi-api-base-BWZYbxxj.js");
3
+ const jsxRuntime = require("react/jsx-runtime");
3
4
  const index = require("./index-CTkxoioB.js");
5
+ const useTranslation = require("./useTranslation-BdIl-aLe.js");
4
6
  const styled = require("styled-components");
5
7
  const React = require("react");
6
- const jsxRuntime = require("react/jsx-runtime");
7
8
  const emptyIdentity = {
8
9
  accessToken: "",
9
10
  project: {
@@ -73,6 +74,17 @@ class PluginSettingsService {
73
74
  throw e;
74
75
  }
75
76
  }
77
+ // Read-gated counterpart for per-user UI prefs (last visited route, sort
78
+ // prefs). Server filters the body to a fixed allowlist; sending anything else
79
+ // is silently dropped, so callers must only pass UI-pref fields here.
80
+ static async updatePluginSettingsUiPrefs(data) {
81
+ try {
82
+ const result = await axiosInstance.put(`${BASE_PATH$1}/ui-prefs`, data);
83
+ return result.data;
84
+ } catch (e) {
85
+ throw e;
86
+ }
87
+ }
76
88
  static async getSyncCursor() {
77
89
  try {
78
90
  const result = await axiosInstance.get(`${BASE_PATH$1}/sync-cursor`);
@@ -82,6 +94,10 @@ class PluginSettingsService {
82
94
  }
83
95
  }
84
96
  }
97
+ function PluginPageLoader() {
98
+ const { t } = useTranslation.useTranslation();
99
+ return /* @__PURE__ */ jsxRuntime.jsx(index.Flex, { marginTop: 10, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(index.Loader, { children: t("common.loading_content") }) });
100
+ }
85
101
  const BASE_PATH = "/user";
86
102
  const strapiApiInstance = strapiApiBase.createStrapiApiAxiosInstance();
87
103
  class LocalazyUserService {
@@ -502,9 +518,9 @@ function cacheHas(cache, key) {
502
518
  return cache.has(key);
503
519
  }
504
520
  var Set = getNative(root, "Set");
505
- function setToArray(set2) {
506
- var index2 = -1, result = Array(set2.size);
507
- set2.forEach(function(value) {
521
+ function setToArray(set) {
522
+ var index2 = -1, result = Array(set.size);
523
+ set.forEach(function(value) {
508
524
  result[++index2] = value;
509
525
  });
510
526
  return result;
@@ -938,32 +954,6 @@ var CLONE_DEEP_FLAG = 1, CLONE_SYMBOLS_FLAG = 4;
938
954
  function cloneDeep(value) {
939
955
  return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
940
956
  }
941
- function baseSet(object, path, value, customizer) {
942
- if (!isObject(object)) {
943
- return object;
944
- }
945
- path = castPath(path, object);
946
- var index2 = -1, length = path.length, lastIndex = length - 1, nested = object;
947
- while (nested != null && ++index2 < length) {
948
- var key = toKey(path[index2]), newValue = value;
949
- if (key === "__proto__" || key === "constructor" || key === "prototype") {
950
- return object;
951
- }
952
- if (index2 != lastIndex) {
953
- var objValue = nested[key];
954
- newValue = void 0;
955
- if (newValue === void 0) {
956
- newValue = isObject(objValue) ? objValue : isIndex(path[index2 + 1]) ? [] : {};
957
- }
958
- }
959
- assignValue(nested, key, newValue);
960
- nested = nested[key];
961
- }
962
- return object;
963
- }
964
- function set(object, path, value) {
965
- return object == null ? object : baseSet(object, path, value);
966
- }
967
957
  const FetchIdentity = () => {
968
958
  const { setIdentity, setIsFetchingIdentity } = useLocalazyIdentity();
969
959
  React.useEffect(() => {
@@ -989,6 +979,7 @@ exports.FetchIdentity = FetchIdentity;
989
979
  exports.HeadingFixGlobalStyle = HeadingFixGlobalStyle;
990
980
  exports.LocalazyIdentityProvider = LocalazyIdentityProvider;
991
981
  exports.LocalazyUserService = LocalazyUserService;
982
+ exports.PluginPageLoader = PluginPageLoader;
992
983
  exports.PluginSettingsService = PluginSettingsService;
993
984
  exports.Set = Set;
994
985
  exports.SetCache = SetCache;
@@ -996,6 +987,7 @@ exports.Stack = Stack;
996
987
  exports.Symbol = Symbol$1;
997
988
  exports.Uint8Array = Uint8Array;
998
989
  exports.arrayEach = arrayEach;
990
+ exports.assignValue = assignValue;
999
991
  exports.cacheHas = cacheHas;
1000
992
  exports.castPath = castPath;
1001
993
  exports.cloneDeep = cloneDeep;
@@ -1006,11 +998,11 @@ exports.getDefaultTheme = getDefaultTheme;
1006
998
  exports.isArray = isArray;
1007
999
  exports.isArrayLike = isArrayLike;
1008
1000
  exports.isBuffer = isBuffer;
1001
+ exports.isIndex = isIndex;
1009
1002
  exports.isObject = isObject;
1010
1003
  exports.isObjectLike = isObjectLike;
1011
1004
  exports.isTypedArray = isTypedArray;
1012
1005
  exports.keys = keys;
1013
- exports.set = set;
1014
1006
  exports.setToArray = setToArray;
1015
1007
  exports.toKey = toKey;
1016
1008
  exports.useLocalazyIdentity = useLocalazyIdentity;
@@ -1,8 +1,9 @@
1
- import { c as createStrapiApiAxiosInstance } from "./strapi-api-base-CAzGcwF6.mjs";
2
- import { V as darkTheme, W as lightTheme } from "./index-BjL9XpRh.mjs";
1
+ import { c as createStrapiApiAxiosInstance } from "./strapi-api-base-lPo7wq6I.mjs";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { c as Flex, L as Loader, V as darkTheme, W as lightTheme } from "./index-shryj0jT.mjs";
4
+ import { u as useTranslation } from "./useTranslation-CZQ4lU1m.mjs";
3
5
  import { createGlobalStyle } from "styled-components";
4
6
  import { useState, useMemo, createContext, useContext, useEffect } from "react";
5
- import { jsx } from "react/jsx-runtime";
6
7
  const emptyIdentity = {
7
8
  accessToken: "",
8
9
  project: {
@@ -72,6 +73,17 @@ class PluginSettingsService {
72
73
  throw e;
73
74
  }
74
75
  }
76
+ // Read-gated counterpart for per-user UI prefs (last visited route, sort
77
+ // prefs). Server filters the body to a fixed allowlist; sending anything else
78
+ // is silently dropped, so callers must only pass UI-pref fields here.
79
+ static async updatePluginSettingsUiPrefs(data) {
80
+ try {
81
+ const result = await axiosInstance.put(`${BASE_PATH$1}/ui-prefs`, data);
82
+ return result.data;
83
+ } catch (e) {
84
+ throw e;
85
+ }
86
+ }
75
87
  static async getSyncCursor() {
76
88
  try {
77
89
  const result = await axiosInstance.get(`${BASE_PATH$1}/sync-cursor`);
@@ -81,6 +93,10 @@ class PluginSettingsService {
81
93
  }
82
94
  }
83
95
  }
96
+ function PluginPageLoader() {
97
+ const { t } = useTranslation();
98
+ return /* @__PURE__ */ jsx(Flex, { marginTop: 10, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx(Loader, { children: t("common.loading_content") }) });
99
+ }
84
100
  const BASE_PATH = "/user";
85
101
  const strapiApiInstance = createStrapiApiAxiosInstance();
86
102
  class LocalazyUserService {
@@ -501,9 +517,9 @@ function cacheHas(cache, key) {
501
517
  return cache.has(key);
502
518
  }
503
519
  var Set = getNative(root, "Set");
504
- function setToArray(set2) {
505
- var index = -1, result = Array(set2.size);
506
- set2.forEach(function(value) {
520
+ function setToArray(set) {
521
+ var index = -1, result = Array(set.size);
522
+ set.forEach(function(value) {
507
523
  result[++index] = value;
508
524
  });
509
525
  return result;
@@ -937,32 +953,6 @@ var CLONE_DEEP_FLAG = 1, CLONE_SYMBOLS_FLAG = 4;
937
953
  function cloneDeep(value) {
938
954
  return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
939
955
  }
940
- function baseSet(object, path, value, customizer) {
941
- if (!isObject(object)) {
942
- return object;
943
- }
944
- path = castPath(path, object);
945
- var index = -1, length = path.length, lastIndex = length - 1, nested = object;
946
- while (nested != null && ++index < length) {
947
- var key = toKey(path[index]), newValue = value;
948
- if (key === "__proto__" || key === "constructor" || key === "prototype") {
949
- return object;
950
- }
951
- if (index != lastIndex) {
952
- var objValue = nested[key];
953
- newValue = void 0;
954
- if (newValue === void 0) {
955
- newValue = isObject(objValue) ? objValue : isIndex(path[index + 1]) ? [] : {};
956
- }
957
- }
958
- assignValue(nested, key, newValue);
959
- nested = nested[key];
960
- }
961
- return object;
962
- }
963
- function set(object, path, value) {
964
- return object == null ? object : baseSet(object, path, value);
965
- }
966
956
  const FetchIdentity = () => {
967
957
  const { setIdentity, setIsFetchingIdentity } = useLocalazyIdentity();
968
958
  useEffect(() => {
@@ -991,10 +981,10 @@ export {
991
981
  PluginSettingsService as P,
992
982
  SetCache as S,
993
983
  Uint8Array as U,
994
- cacheHas as a,
995
- Symbol$1 as b,
984
+ PluginPageLoader as a,
985
+ cacheHas as b,
996
986
  cloneDeep as c,
997
- setToArray as d,
987
+ Symbol$1 as d,
998
988
  eq as e,
999
989
  getAllKeys as f,
1000
990
  getDefaultTheme as g,
@@ -1004,15 +994,17 @@ export {
1004
994
  isTypedArray as k,
1005
995
  isArray as l,
1006
996
  isObjectLike as m,
1007
- keys as n,
1008
- isArrayLike as o,
1009
- arrayEach as p,
1010
- isObject as q,
1011
- castPath as r,
1012
- set as s,
997
+ isObject as n,
998
+ castPath as o,
999
+ isIndex as p,
1000
+ assignValue as q,
1001
+ keys as r,
1002
+ setToArray as s,
1013
1003
  toKey as t,
1014
1004
  useLocalazyIdentity as u,
1015
- Set as v,
1016
- LocalazyUserService as w,
1017
- emptyIdentity as x
1005
+ isArrayLike as v,
1006
+ arrayEach as w,
1007
+ Set as x,
1008
+ LocalazyUserService as y,
1009
+ emptyIdentity as z
1018
1010
  };
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
- const strapiApiBase = require("./strapi-api-base-IG3Pe5Sg.js");
2
+ const strapiApiBase = require("./strapi-api-base-BWZYbxxj.js");
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const React = require("react");
5
- const FetchIdentity = require("./FetchIdentity-BpFOT163.js");
5
+ const FetchIdentity = require("./FetchIdentity-D89DgpbN.js");
6
6
  const index = require("./index-CTkxoioB.js");
7
7
  const useTranslation = require("./useTranslation-BdIl-aLe.js");
8
8
  const strapiApiInstance = strapiApiBase.createStrapiApiAxiosInstance();
@@ -49,6 +49,7 @@ const LanguagesSelector = (props) => {
49
49
  onClear: () => setSelectedLanguages([]),
50
50
  value: selectedLanguages || [],
51
51
  onChange: (values) => onChange(values),
52
+ disabled: props.disabled,
52
53
  multi: true,
53
54
  withTags: true,
54
55
  children: props.projectLanguages.map((lang) => /* @__PURE__ */ jsxRuntime.jsx(index.MultiSelectOption, { value: lang.code, children: `${lang.name} (${lang.code})` }, lang.id))
@@ -1,8 +1,8 @@
1
- import { c as createStrapiApiAxiosInstance } from "./strapi-api-base-CAzGcwF6.mjs";
1
+ import { c as createStrapiApiAxiosInstance } from "./strapi-api-base-lPo7wq6I.mjs";
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
3
  import { useState, useEffect } from "react";
4
- import { c as cloneDeep } from "./FetchIdentity-Cor4ooZv.mjs";
5
- import { F as Field, M as MultiSelect, b as MultiSelectOption } from "./index-BjL9XpRh.mjs";
4
+ import { c as cloneDeep } from "./FetchIdentity-DkHj24S5.mjs";
5
+ import { F as Field, M as MultiSelect, b as MultiSelectOption } from "./index-shryj0jT.mjs";
6
6
  import { u as useTranslation } from "./useTranslation-CZQ4lU1m.mjs";
7
7
  const strapiApiInstance = createStrapiApiAxiosInstance();
8
8
  class ProjectService {
@@ -48,6 +48,7 @@ const LanguagesSelector = (props) => {
48
48
  onClear: () => setSelectedLanguages([]),
49
49
  value: selectedLanguages || [],
50
50
  onChange: (values) => onChange(values),
51
+ disabled: props.disabled,
51
52
  multi: true,
52
53
  withTags: true,
53
54
  children: props.projectLanguages.map((lang) => /* @__PURE__ */ jsx(MultiSelectOption, { value: lang.code, children: `${lang.name} (${lang.code})` }, lang.id))
@@ -4,15 +4,15 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const reactRouterDom = require("react-router-dom");
5
5
  const index$1 = require("./index-CTkxoioB.js");
6
6
  const React = require("react");
7
- const FetchIdentity = require("./FetchIdentity-BpFOT163.js");
8
- const index = require("./index-jAg6jjCu.js");
7
+ const FetchIdentity = require("./FetchIdentity-D89DgpbN.js");
8
+ const index = require("./index-DwFm1TyM.js");
9
9
  const useTranslation = require("./useTranslation-BdIl-aLe.js");
10
10
  const _commonjsHelpers = require("./_commonjsHelpers-Bc2YnDe1.js");
11
- const strapiApiBase = require("./strapi-api-base-IG3Pe5Sg.js");
12
- const LanguagesSelector = require("./LanguagesSelector-CC1ecWnc.js");
13
11
  const admin = require("@strapi/strapi/admin");
14
- require("./i18n-C_5tJvr0.js");
15
- const hasModelChanged = require("./has-model-changed-5IV9WI_-.js");
12
+ const strapiApiBase = require("./strapi-api-base-BWZYbxxj.js");
13
+ const LanguagesSelector = require("./LanguagesSelector-638KGQYw.js");
14
+ require("./i18n-C5ucROBh.js");
15
+ const hasModelChanged = require("./has-model-changed-BrDeyHFt.js");
16
16
  const styled = require("styled-components");
17
17
  const _interopDefault = (e2) => e2 && e2.__esModule ? e2 : { default: e2 };
18
18
  const React__default = /* @__PURE__ */ _interopDefault(React);
@@ -102,10 +102,6 @@ const useHeaderSubtitle = () => {
102
102
  const currentNavItem = navigation.find((navItem) => pluginSegment.includes(navItem.id));
103
103
  return currentNavItem?.description || "";
104
104
  };
105
- function PluginPageLoader() {
106
- const { t: t2 } = useTranslation.useTranslation();
107
- return /* @__PURE__ */ jsxRuntime.jsx(index$1.Flex, { marginTop: 10, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxRuntime.jsx(index$1.Loader, { children: t2("common.loading_content") }) });
108
- }
109
105
  var propTypes = { exports: {} };
110
106
  var reactIs = { exports: {} };
111
107
  var reactIs_production_min = {};
@@ -1262,6 +1258,8 @@ if (process.env.STRAPI_ADMIN_LOCALAZY_ENV === "development") {
1262
1258
  }
1263
1259
  const LoginButton = (props) => {
1264
1260
  const { t: t2 } = useTranslation.useTranslation();
1261
+ const { allowedActions } = admin.useRBAC(index.PERMISSIONS.SETTINGS_UPDATE);
1262
+ const canConnect = !!allowedActions.canUpdate;
1265
1263
  const [isLoading, setIsLoading] = React.useState(false);
1266
1264
  const login = async () => {
1267
1265
  setIsLoading(true);
@@ -1282,7 +1280,8 @@ const LoginButton = (props) => {
1282
1280
  setIsLoading(false);
1283
1281
  props.onResultFetched(pollResult);
1284
1282
  };
1285
- return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(index$1.Button, { variant: "default", size: "L", loading: isLoading, onClick: login, children: t2("login.login_with_localazy") }) });
1283
+ const button = /* @__PURE__ */ jsxRuntime.jsx(index$1.Button, { variant: "default", size: "L", loading: isLoading, onClick: login, disabled: !canConnect, children: t2("login.login_with_localazy") });
1284
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: canConnect ? button : /* @__PURE__ */ jsxRuntime.jsx(index$1.TooltipImpl, { label: t2("login.requires_settings_update_permission"), children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: button }) }) });
1286
1285
  };
1287
1286
  LoginButton.propTypes = {
1288
1287
  onResultFetched: PropTypes.func.isRequired
@@ -1396,7 +1395,7 @@ const Login = ({ title, subtitle, isLoading = false }) => {
1396
1395
  }, [isLoggedIn, identity]);
1397
1396
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1398
1397
  /* @__PURE__ */ jsxRuntime.jsx(admin.Layouts.Header, { title, subtitle, as: "h1" }),
1399
- isLoading && /* @__PURE__ */ jsxRuntime.jsx(PluginPageLoader, {}),
1398
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.PluginPageLoader, {}),
1400
1399
  !isLoading && /* @__PURE__ */ jsxRuntime.jsxs(
1401
1400
  index$1.Box,
1402
1401
  {
@@ -1440,6 +1439,8 @@ const LogoutButton = (props) => {
1440
1439
  const { t: t2 } = useTranslation.useTranslation();
1441
1440
  const [isLoading, setIsLoading] = React.useState(false);
1442
1441
  const { setIdentity } = FetchIdentity.useLocalazyIdentity();
1442
+ const { allowedActions } = admin.useRBAC(index.PERMISSIONS.SETTINGS_UPDATE);
1443
+ const canDisconnect = !!allowedActions.canUpdate;
1443
1444
  const logout = async () => {
1444
1445
  setIsLoading(true);
1445
1446
  void FetchIdentity.LocalazyUserService.deleteIdentity();
@@ -1447,7 +1448,8 @@ const LogoutButton = (props) => {
1447
1448
  setIsLoading(false);
1448
1449
  props.onResultFetched();
1449
1450
  };
1450
- return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(index$1.Button, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(index$1.ForwardRef$L, {}), variant: "secondary", loading: isLoading, onClick: logout, children: t2("login.logout_from_localazy") }) });
1451
+ const button = /* @__PURE__ */ jsxRuntime.jsx(index$1.Button, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(index$1.ForwardRef$L, {}), variant: "secondary", loading: isLoading, onClick: logout, disabled: !canDisconnect, children: t2("login.logout_from_localazy") });
1452
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: canDisconnect ? button : /* @__PURE__ */ jsxRuntime.jsx(index$1.TooltipImpl, { label: t2("login.requires_settings_update_permission"), children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: button }) }) });
1451
1453
  };
1452
1454
  const OverviewItem = (props) => {
1453
1455
  return /* @__PURE__ */ jsxRuntime.jsxs(index$1.Grid.Root, { padding: 1, col: 6, s: 12, children: [
@@ -1478,7 +1480,7 @@ const Overview = ({ title, subtitle, isLoadingProp = false }) => {
1478
1480
  setIsLoading(true);
1479
1481
  const project = await LanguagesSelector.ProjectService.getConnectedProject();
1480
1482
  setConnectedProject(project);
1481
- void FetchIdentity.PluginSettingsService.updatePluginSettings({ defaultRoute: PLUGIN_ROUTES.OVERVIEW });
1483
+ void FetchIdentity.PluginSettingsService.updatePluginSettingsUiPrefs({ defaultRoute: PLUGIN_ROUTES.OVERVIEW });
1482
1484
  setIsLoading(false);
1483
1485
  }
1484
1486
  void initComponent();
@@ -1496,7 +1498,7 @@ const Overview = ({ title, subtitle, isLoadingProp = false }) => {
1496
1498
  primaryAction: /* @__PURE__ */ jsxRuntime.jsx(LogoutButton, { onResultFetched: onLoggedOut })
1497
1499
  }
1498
1500
  ),
1499
- (isLoadingProp || isLoading) && /* @__PURE__ */ jsxRuntime.jsx(PluginPageLoader, {}),
1501
+ (isLoadingProp || isLoading) && /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.PluginPageLoader, {}),
1500
1502
  !(isLoadingProp || isLoading) && /* @__PURE__ */ jsxRuntime.jsx(index$1.Box, { paddingRight: 10, paddingLeft: 10, children: /* @__PURE__ */ jsxRuntime.jsxs(
1501
1503
  index$1.Box,
1502
1504
  {
@@ -5065,7 +5067,7 @@ const Upload = (props) => {
5065
5067
  setLocalazySourceLanguage(sourceLanguage);
5066
5068
  setModelChanged(modelChangedResult);
5067
5069
  setLocalesIncompatible(!localesCompatibleResult);
5068
- void FetchIdentity.PluginSettingsService.updatePluginSettings({ defaultRoute: PLUGIN_ROUTES.UPLOAD });
5070
+ void FetchIdentity.PluginSettingsService.updatePluginSettingsUiPrefs({ defaultRoute: PLUGIN_ROUTES.UPLOAD });
5069
5071
  setIsLoading(false);
5070
5072
  }
5071
5073
  void initComponent();
@@ -5088,7 +5090,7 @@ const Upload = (props) => {
5088
5090
  )
5089
5091
  }
5090
5092
  ),
5091
- isLoading && /* @__PURE__ */ jsxRuntime.jsx(PluginPageLoader, {}),
5093
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.PluginPageLoader, {}),
5092
5094
  !isLoading && /* @__PURE__ */ jsxRuntime.jsxs(index$1.Box, { paddingRight: 10, paddingLeft: 10, children: [
5093
5095
  /* @__PURE__ */ jsxRuntime.jsxs(
5094
5096
  index$1.Box,
@@ -5198,16 +5200,9 @@ const Download = (props) => {
5198
5200
  window.open(identity.project.url, "_blank");
5199
5201
  };
5200
5202
  const [projectLanguages, setProjectLanguages] = React.useState([]);
5201
- const [formModel, setFormModel] = React.useState({});
5203
+ const [uiLanguages, setUiLanguages] = React.useState([]);
5202
5204
  const onDownloadLanguagesChange = (languages) => {
5203
- const newFormModel = FetchIdentity.cloneDeep(formModel);
5204
- FetchIdentity.set(newFormModel, "download.uiLanguages", languages);
5205
- setFormModel(newFormModel);
5206
- try {
5207
- void FetchIdentity.PluginSettingsService.updatePluginSettings(newFormModel);
5208
- } catch (e2) {
5209
- console.error(e2.message);
5210
- }
5205
+ setUiLanguages(languages);
5211
5206
  };
5212
5207
  const onDownloadClick = async (fullSync = false) => {
5213
5208
  setIsDownloading(true);
@@ -5216,7 +5211,7 @@ const Download = (props) => {
5216
5211
  success: false,
5217
5212
  report: []
5218
5213
  });
5219
- const result = await LocalazyDownloadService.download({ fullSync });
5214
+ const result = await LocalazyDownloadService.download({ fullSync, uiLanguages });
5220
5215
  const { streamIdentifier } = result;
5221
5216
  downloadAlertsService.setStreamIdentifier(streamIdentifier);
5222
5217
  downloadAlertsService.onDownload((data) => {
@@ -5257,8 +5252,8 @@ const Download = (props) => {
5257
5252
  setHasSyncCursor(syncCursor?.processedKeys !== void 0 && Object.keys(syncCursor.processedKeys).length > 0);
5258
5253
  const projectLanguagesWithoutDefaultLanguage = project?.languages?.filter((language) => language.id !== project.sourceLanguage) || [];
5259
5254
  setProjectLanguages(projectLanguagesWithoutDefaultLanguage);
5260
- setFormModel(globalSettings);
5261
- void FetchIdentity.PluginSettingsService.updatePluginSettings({ defaultRoute: PLUGIN_ROUTES.DOWNLOAD });
5255
+ setUiLanguages(Array.isArray(globalSettings?.download?.uiLanguages) ? globalSettings.download.uiLanguages : []);
5256
+ void FetchIdentity.PluginSettingsService.updatePluginSettingsUiPrefs({ defaultRoute: PLUGIN_ROUTES.DOWNLOAD });
5262
5257
  setIsLoading(false);
5263
5258
  }
5264
5259
  void initComponent();
@@ -5293,7 +5288,7 @@ const Download = (props) => {
5293
5288
  ] })
5294
5289
  }
5295
5290
  ),
5296
- isLoading && /* @__PURE__ */ jsxRuntime.jsx(PluginPageLoader, {}),
5291
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.PluginPageLoader, {}),
5297
5292
  !isLoading && /* @__PURE__ */ jsxRuntime.jsxs(index$1.Box, { paddingRight: 10, paddingLeft: 10, children: [
5298
5293
  /* @__PURE__ */ jsxRuntime.jsxs(
5299
5294
  index$1.Box,
@@ -5316,7 +5311,7 @@ const Download = (props) => {
5316
5311
  /* @__PURE__ */ jsxRuntime.jsx(index$1.Box, { paddingTop: 6, children: /* @__PURE__ */ jsxRuntime.jsx(
5317
5312
  LanguagesSelector.LanguagesSelector,
5318
5313
  {
5319
- preselectedLanguages: formModel?.download?.uiLanguages || [],
5314
+ preselectedLanguages: uiLanguages,
5320
5315
  projectLanguages,
5321
5316
  onChange: (languages) => onDownloadLanguagesChange(languages),
5322
5317
  label: t2("download.ui_languages"),
@@ -5693,6 +5688,9 @@ const useDebouncedSearch = () => {
5693
5688
  const ActivityLogs = (props) => {
5694
5689
  const { t: t2 } = useTranslation.useTranslation();
5695
5690
  const navigate = reactRouterDom.useNavigate();
5691
+ const {
5692
+ allowedActions: { canTransfer }
5693
+ } = admin.useRBAC(index.PERMISSIONS.TRANSFER);
5696
5694
  const [isLoading, setIsLoading] = React.useState(true);
5697
5695
  const [sessions, setSessions] = React.useState([]);
5698
5696
  const [activeTab, setActiveTab] = React.useState("upload");
@@ -5706,7 +5704,7 @@ const ActivityLogs = (props) => {
5706
5704
  const debouncedSaveSortPrefs = React.useCallback((prefs) => {
5707
5705
  if (saveSortTimerRef.current) clearTimeout(saveSortTimerRef.current);
5708
5706
  saveSortTimerRef.current = setTimeout(() => {
5709
- void FetchIdentity.PluginSettingsService.updatePluginSettings({ activityLogsSort: prefs });
5707
+ void FetchIdentity.PluginSettingsService.updatePluginSettingsUiPrefs({ activityLogsSort: prefs });
5710
5708
  }, 1e3);
5711
5709
  }, []);
5712
5710
  const onSortChange = (eventType, key, direction) => {
@@ -5762,7 +5760,7 @@ const ActivityLogs = (props) => {
5762
5760
  void fetchSessions("upload");
5763
5761
  };
5764
5762
  void init();
5765
- void FetchIdentity.PluginSettingsService.updatePluginSettings({ defaultRoute: PLUGIN_ROUTES.ACTIVITY_LOGS });
5763
+ void FetchIdentity.PluginSettingsService.updatePluginSettingsUiPrefs({ defaultRoute: PLUGIN_ROUTES.ACTIVITY_LOGS });
5766
5764
  }, []);
5767
5765
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5768
5766
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5772,7 +5770,16 @@ const ActivityLogs = (props) => {
5772
5770
  subtitle: props.subtitle,
5773
5771
  primaryAction: /* @__PURE__ */ jsxRuntime.jsxs(index$1.Flex, { gap: 2, children: [
5774
5772
  /* @__PURE__ */ jsxRuntime.jsx(index$1.Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsxRuntime.jsx(index$1.ForwardRef$3P, {}), onClick: onExportLogs, children: t2("activity_logs.export_logs") }),
5775
- /* @__PURE__ */ jsxRuntime.jsx(index$1.Button, { variant: "danger-light", startIcon: /* @__PURE__ */ jsxRuntime.jsx(index$1.ForwardRef$j, {}), onClick: () => setShowClearConfirm(true), children: t2("activity_logs.clear_logs") })
5773
+ /* @__PURE__ */ jsxRuntime.jsx(
5774
+ index$1.Button,
5775
+ {
5776
+ variant: "danger-light",
5777
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(index$1.ForwardRef$j, {}),
5778
+ disabled: !canTransfer,
5779
+ onClick: () => setShowClearConfirm(true),
5780
+ children: t2("activity_logs.clear_logs")
5781
+ }
5782
+ )
5776
5783
  ] })
5777
5784
  }
5778
5785
  ),
@@ -5780,7 +5787,7 @@ const ActivityLogs = (props) => {
5780
5787
  /* @__PURE__ */ jsxRuntime.jsx(index$1.ForwardRef$2x, { width: 14, height: 14, fill: "neutral500" }),
5781
5788
  /* @__PURE__ */ jsxRuntime.jsx(index$1.Typography, { variant: "pi", textColor: "neutral500", children: t2("activity_logs.retention_info") })
5782
5789
  ] }) }),
5783
- isLoading && /* @__PURE__ */ jsxRuntime.jsx(PluginPageLoader, {}),
5790
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.PluginPageLoader, {}),
5784
5791
  !isLoading && /* @__PURE__ */ jsxRuntime.jsxs(index$1.Box, { paddingRight: 10, paddingLeft: 10, children: [
5785
5792
  clearSuccess && /* @__PURE__ */ jsxRuntime.jsx(index$1.Box, { marginBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(index$1.Alert, { onClose: () => setClearSuccess(false), closeLabel: t2("activity_logs.close"), variant: "success", children: t2("activity_logs.clear_logs_success") }) }),
5786
5793
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -6043,7 +6050,7 @@ const ActivityLogDetail = (props) => {
6043
6050
  ) })
6044
6051
  }
6045
6052
  ),
6046
- isLoading && /* @__PURE__ */ jsxRuntime.jsx(PluginPageLoader, {}),
6053
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.PluginPageLoader, {}),
6047
6054
  !isLoading && !session && /* @__PURE__ */ jsxRuntime.jsx(index$1.Box, { paddingRight: 10, paddingLeft: 10, children: /* @__PURE__ */ jsxRuntime.jsx(index$1.Typography, { variant: "omega", children: t2("activity_logs.no_sessions") }) }),
6048
6055
  !isLoading && session && /* @__PURE__ */ jsxRuntime.jsxs(index$1.Box, { paddingRight: 10, paddingLeft: 10, children: [
6049
6056
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -6140,6 +6147,10 @@ const App = () => {
6140
6147
  const { isLoggedIn, isFetchingIdentity } = FetchIdentity.useLocalazyIdentity();
6141
6148
  const headerTitle = useHeaderTitle();
6142
6149
  const headerSubtitle = useHeaderSubtitle();
6150
+ const {
6151
+ allowedActions: { canRead, canTransfer },
6152
+ isLoading: isLoadingPermissions
6153
+ } = admin.useRBAC([...index.PERMISSIONS.READ, ...index.PERMISSIONS.TRANSFER]);
6143
6154
  const normalizedPath = location2.pathname.replace(/\/+$/, "");
6144
6155
  const pluginRoot = `/plugins/${index.PLUGIN_ID}`;
6145
6156
  const isAtPluginRoot = normalizedPath === pluginRoot;
@@ -6161,10 +6172,23 @@ const App = () => {
6161
6172
  };
6162
6173
  void fetchData();
6163
6174
  }, [isLoggedIn, isFetchingIdentity, normalizedPath]);
6175
+ if (isLoadingPermissions) {
6176
+ return /* @__PURE__ */ jsxRuntime.jsxs(index$1.DesignSystemProvider, { theme: FetchIdentity.getDefaultTheme(), children: [
6177
+ /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.HeadingFixGlobalStyle, {}),
6178
+ /* @__PURE__ */ jsxRuntime.jsx(index$1.Box, { background: "neutral100", children: /* @__PURE__ */ jsxRuntime.jsx(admin.Layouts.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.PluginPageLoader, {}) }) })
6179
+ ] });
6180
+ }
6181
+ if (!canRead) {
6182
+ return /* @__PURE__ */ jsxRuntime.jsxs(index$1.DesignSystemProvider, { theme: FetchIdentity.getDefaultTheme(), children: [
6183
+ /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.HeadingFixGlobalStyle, {}),
6184
+ /* @__PURE__ */ jsxRuntime.jsx(index$1.Box, { background: "neutral100", children: /* @__PURE__ */ jsxRuntime.jsx(admin.Layouts.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.NoPermissions, {}) }) })
6185
+ ] });
6186
+ }
6187
+ const transferElement = (page) => canTransfer ? page : /* @__PURE__ */ jsxRuntime.jsx(admin.Page.NoPermissions, {});
6164
6188
  return /* @__PURE__ */ jsxRuntime.jsxs(index$1.DesignSystemProvider, { theme: FetchIdentity.getDefaultTheme(), children: [
6165
6189
  /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.HeadingFixGlobalStyle, {}),
6166
6190
  /* @__PURE__ */ jsxRuntime.jsx(PluginErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(index$1.Box, { background: "neutral100", children: /* @__PURE__ */ jsxRuntime.jsxs(admin.Layouts.Root, { sideNav: isLoggedIn && /* @__PURE__ */ jsxRuntime.jsx(SideNav, {}), children: [
6167
- isFetchingIdentity && /* @__PURE__ */ jsxRuntime.jsx(PluginPageLoader, {}),
6191
+ isFetchingIdentity && /* @__PURE__ */ jsxRuntime.jsx(FetchIdentity.PluginPageLoader, {}),
6168
6192
  /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
6169
6193
  /* @__PURE__ */ jsxRuntime.jsx(
6170
6194
  reactRouterDom.Route,
@@ -6180,8 +6204,20 @@ const App = () => {
6180
6204
  element: /* @__PURE__ */ jsxRuntime.jsx(Overview, { title: headerTitle, subtitle: headerSubtitle, isLoadingProp: false })
6181
6205
  }
6182
6206
  ),
6183
- /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: `upload`, element: /* @__PURE__ */ jsxRuntime.jsx(Upload, { title: headerTitle, subtitle: headerSubtitle }) }),
6184
- /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: `download`, element: /* @__PURE__ */ jsxRuntime.jsx(Download, { title: headerTitle, subtitle: headerSubtitle }) }),
6207
+ /* @__PURE__ */ jsxRuntime.jsx(
6208
+ reactRouterDom.Route,
6209
+ {
6210
+ path: `upload`,
6211
+ element: transferElement(/* @__PURE__ */ jsxRuntime.jsx(Upload, { title: headerTitle, subtitle: headerSubtitle }))
6212
+ }
6213
+ ),
6214
+ /* @__PURE__ */ jsxRuntime.jsx(
6215
+ reactRouterDom.Route,
6216
+ {
6217
+ path: `download`,
6218
+ element: transferElement(/* @__PURE__ */ jsxRuntime.jsx(Download, { title: headerTitle, subtitle: headerSubtitle }))
6219
+ }
6220
+ ),
6185
6221
  /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: `activity-logs`, element: /* @__PURE__ */ jsxRuntime.jsx(ActivityLogs, { title: headerTitle, subtitle: headerSubtitle }) }),
6186
6222
  /* @__PURE__ */ jsxRuntime.jsx(
6187
6223
  reactRouterDom.Route,