@strapi/admin 4.12.4 → 4.12.6

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 (187) hide show
  1. package/admin/src/StrapiApp.js +1 -1
  2. package/admin/src/components/AuthenticatedApp.js +229 -0
  3. package/admin/src/components/GuidedTour/Modal/index.js +1 -3
  4. package/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js +7 -30
  5. package/admin/src/content-manager/hooks/useSyncRbac/index.js +10 -2
  6. package/admin/src/content-manager/pages/App/index.js +5 -16
  7. package/admin/src/content-manager/pages/CollectionTypeRecursivePath/index.js +1 -1
  8. package/admin/src/content-manager/pages/EditView/Information/index.js +1 -1
  9. package/admin/src/content-manager/pages/EditViewLayoutManager/index.js +2 -2
  10. package/admin/src/content-manager/pages/ListSettingsView/index.js +16 -41
  11. package/admin/src/content-manager/pages/ListView/components/BulkActionButtons/SelectedEntriesModal/index.js +2 -2
  12. package/admin/src/content-manager/pages/ListView/components/TableRows/index.js +1 -1
  13. package/admin/src/content-manager/pages/ListView/index.js +27 -1
  14. package/admin/src/content-manager/pages/ListViewLayoutManager/index.js +2 -2
  15. package/admin/src/hooks/useSettingsForm/index.js +14 -3
  16. package/admin/src/hooks/useSettingsMenu/index.js +2 -2
  17. package/admin/src/hooks/useSettingsMenu/utils/formatLinks.js +1 -3
  18. package/admin/src/hooks/useSettingsMenu/utils/sortLinks.js +1 -3
  19. package/admin/src/index.js +1 -1
  20. package/admin/src/pages/Admin/Onboarding/index.js +1 -3
  21. package/admin/src/pages/Admin/index.js +80 -74
  22. package/admin/src/pages/App/constants.js +1 -1
  23. package/admin/src/pages/App/index.js +160 -122
  24. package/admin/src/pages/AuthPage/index.js +2 -4
  25. package/admin/src/pages/HomePage/index.js +1 -3
  26. package/admin/src/pages/InstalledPluginsPage/index.js +1 -3
  27. package/admin/src/pages/{InternalErrorPage/index.js → InternalErrorPage.js} +3 -4
  28. package/admin/src/pages/MarketplacePage/index.js +0 -1
  29. package/admin/src/pages/{NotFoundPage/index.js → NotFoundPage.js} +1 -3
  30. package/admin/src/pages/ProfilePage/index.js +2 -4
  31. package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +3 -3
  32. package/admin/src/pages/SettingsPage/constants.js +67 -132
  33. package/admin/src/pages/SettingsPage/index.js +31 -36
  34. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +1 -1
  35. package/admin/src/pages/SettingsPage/pages/ApiTokens/ListView/index.js +24 -31
  36. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +69 -35
  37. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/index.js +1 -1
  38. package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/index.js +11 -6
  39. package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +4 -1
  40. package/admin/src/pages/SettingsPage/pages/Users/ListPage/DynamicTable/TableRows/index.js +1 -1
  41. package/admin/src/pages/SettingsPage/pages/Users/ListPage/index.js +1 -0
  42. package/admin/src/pages/UseCasePage.js +174 -0
  43. package/admin/src/translations/zh-Hans.json +918 -902
  44. package/admin/src/utils/createRoute.js +5 -7
  45. package/admin/src/utils/formatAPIErrors.js +1 -3
  46. package/admin/src/utils/getFullName.js +1 -1
  47. package/admin/src/utils/sortLinks.js +1 -3
  48. package/admin/src/utils/uniqueAdminHash.js +2 -9
  49. package/build/{1049.f76cb14b.chunk.js → 1049.9d69d231.chunk.js} +1 -1
  50. package/build/1504.eff012f7.chunk.js +95 -0
  51. package/build/2166.c837469a.chunk.js +1 -0
  52. package/build/2225.33287e1b.chunk.js +79 -0
  53. package/build/2237.03792b63.chunk.js +114 -0
  54. package/build/2379.401f56f3.chunk.js +1 -0
  55. package/build/2395.e6a79fbb.chunk.js +26 -0
  56. package/build/{9806.3392505e.chunk.js → 2747.d1442a90.chunk.js} +78 -70
  57. package/build/2801.31393ffe.chunk.js +1 -0
  58. package/build/3483.8517171f.chunk.js +1 -0
  59. package/build/4546.7a3c0d03.chunk.js +1 -0
  60. package/build/502.8dd074ff.chunk.js +1 -0
  61. package/build/5483.5bfbb00d.chunk.js +6 -0
  62. package/build/7464.592a9295.chunk.js +1 -0
  63. package/build/748.fd2e5afd.chunk.js +105 -0
  64. package/build/773.6381d62d.chunk.js +18 -0
  65. package/build/7826.399afe81.chunk.js +103 -0
  66. package/build/8261.2525d35c.chunk.js +7 -0
  67. package/build/8276.e519a707.chunk.js +26 -0
  68. package/build/8299.62b67c72.chunk.js +1 -0
  69. package/build/Admin-AuthPage.90d64342.chunk.js +35 -0
  70. package/build/Admin-AuthenticatedApp.379ac945.chunk.js +24 -0
  71. package/build/Admin-UseCasePage.1f757db5.chunk.js +13 -0
  72. package/build/Admin_GuidedTourModal.8ccf1fbc.chunk.js +12 -0
  73. package/build/Admin_InternalErrorPage.9de92c6d.chunk.js +9 -0
  74. package/build/Admin_NotFoundPage.21620424.chunk.js +9 -0
  75. package/build/Admin_Onboarding.dbfa32f6.chunk.js +43 -0
  76. package/build/Admin_homePage.2000cbe9.chunk.js +86 -0
  77. package/build/Admin_marketplace.ec80e29b.chunk.js +63 -0
  78. package/build/Admin_pluginsPage.0c6851f8.chunk.js +14 -0
  79. package/build/Admin_profilePage.78cd8495.chunk.js +21 -0
  80. package/build/Admin_settingsPage.1760c3ce.chunk.js +119 -0
  81. package/build/StrapiApp.221fac30.chunk.js +5 -0
  82. package/build/{admin-edit-roles-page.6d567273.chunk.js → admin-edit-roles-page.24bdf746.chunk.js} +1 -1
  83. package/build/admin-edit-users.5d10d444.chunk.js +10 -0
  84. package/build/admin-users.2b3e4305.chunk.js +11 -0
  85. package/build/api-tokens-list-page.0af7d431.chunk.js +16 -0
  86. package/build/audit-logs-settings-page.0f73ccf8.chunk.js +1 -0
  87. package/build/content-manager.fb0833bd.chunk.js +1099 -0
  88. package/build/{content-type-builder.40534de5.chunk.js → content-type-builder.66066281.chunk.js} +18 -18
  89. package/build/email-settings-page.2f7e35c0.chunk.js +11 -0
  90. package/build/i18n-translation-ru-json.a3dbc125.chunk.js +1 -0
  91. package/build/index.html +1 -1
  92. package/build/main.ee3c1938.js +2859 -0
  93. package/build/review-workflows-settings-create-view.d24a32b9.chunk.js +1 -0
  94. package/build/review-workflows-settings-edit-view.6044b022.chunk.js +1 -0
  95. package/build/review-workflows-settings-list-view.3f0ef4bc.chunk.js +56 -0
  96. package/build/runtime~main.397ee447.js +2 -0
  97. package/build/{sso-settings-page.12b6d8ae.chunk.js → sso-settings-page.4dba0670.chunk.js} +1 -1
  98. package/build/transfer-tokens-list-page.d6986b03.chunk.js +16 -0
  99. package/build/users-advanced-settings-page.17052d72.chunk.js +9 -0
  100. package/build/users-email-settings-page.3de8ea50.chunk.js +9 -0
  101. package/build/users-permissions-translation-zh-Hans-json.8d82c809.chunk.js +1 -0
  102. package/build/users-providers-settings-page.0eaa916d.chunk.js +14 -0
  103. package/build/users-roles-settings-page.957ad48b.chunk.js +55 -0
  104. package/build/webhook-edit-page.665210af.chunk.js +33 -0
  105. package/build/zh-Hans-json.97efd015.chunk.js +1 -0
  106. package/ee/admin/hooks/useAuthProviders.js +25 -0
  107. package/ee/admin/hooks/{useLicenseLimitNotification/index.js → useLicenseLimitNotification.js} +2 -4
  108. package/ee/admin/hooks/{useLicenseLimits/useLicenseLimits.js → useLicenseLimits.js} +4 -1
  109. package/ee/admin/pages/App/constants.js +6 -5
  110. package/ee/admin/pages/AuthPage/components/Login/index.js +8 -4
  111. package/ee/admin/pages/AuthPage/components/Providers/index.js +8 -5
  112. package/ee/admin/pages/HomePage/index.js +1 -1
  113. package/ee/admin/pages/SettingsPage/constants.js +27 -42
  114. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +1 -1
  115. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +1 -1
  116. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js +1 -1
  117. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +1 -1
  118. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/index.js +1 -1
  119. package/package.json +10 -10
  120. package/admin/src/components/AuthenticatedApp/index.js +0 -118
  121. package/admin/src/components/AuthenticatedApp/utils/api.js +0 -85
  122. package/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.js +0 -11
  123. package/admin/src/components/PluginsInitializer/index.js +0 -68
  124. package/admin/src/components/PluginsInitializer/init.js +0 -11
  125. package/admin/src/components/PluginsInitializer/reducer.js +0 -22
  126. package/admin/src/layouts/AppLayout/index.js +0 -33
  127. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js +0 -23
  128. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/prefixAllUrls.js +0 -17
  129. package/admin/src/pages/SettingsPage/utils/createSectionsRoutes.js +0 -11
  130. package/admin/src/pages/SettingsPage/utils/getSectionsToDisplay.js +0 -5
  131. package/admin/src/pages/SettingsPage/utils/index.js +0 -2
  132. package/admin/src/pages/UseCasePage/index.js +0 -175
  133. package/admin/src/utils/checkFormValidity.js +0 -15
  134. package/admin/src/utils/getAttributesToDisplay.js +0 -19
  135. package/admin/src/utils/getExistingActions.js +0 -32
  136. package/admin/src/utils/index.js +0 -9
  137. package/admin/src/utils/makeUniqueRoutes.js +0 -6
  138. package/build/1386.879bcd90.chunk.js +0 -7
  139. package/build/2225.c6244756.chunk.js +0 -79
  140. package/build/2379.f1641312.chunk.js +0 -1
  141. package/build/2395.46f8d0c1.chunk.js +0 -26
  142. package/build/2801.5cef5ec8.chunk.js +0 -1
  143. package/build/3483.03c24f96.chunk.js +0 -1
  144. package/build/3739.63e352f1.chunk.js +0 -103
  145. package/build/3929.5632f24d.chunk.js +0 -114
  146. package/build/448.829e1344.chunk.js +0 -1
  147. package/build/4546.cfafae68.chunk.js +0 -1
  148. package/build/502.8ae8ef60.chunk.js +0 -1
  149. package/build/5483.6dd2e776.chunk.js +0 -6
  150. package/build/5542.2415a393.chunk.js +0 -63
  151. package/build/6691.4985ef22.chunk.js +0 -105
  152. package/build/7464.3e64a1d5.chunk.js +0 -1
  153. package/build/8276.10a3f883.chunk.js +0 -26
  154. package/build/9944.7af075a5.chunk.js +0 -26
  155. package/build/Admin-authenticatedApp.f5ece8ff.chunk.js +0 -79
  156. package/build/Admin_InternalErrorPage.f45f2462.chunk.js +0 -1
  157. package/build/Admin_homePage.ac9dfb86.chunk.js +0 -81
  158. package/build/Admin_marketplace.dde9c148.chunk.js +0 -55
  159. package/build/Admin_pluginsPage.bbe79434.chunk.js +0 -6
  160. package/build/Admin_profilePage.192edc52.chunk.js +0 -13
  161. package/build/Admin_settingsPage.97cb9d41.chunk.js +0 -111
  162. package/build/admin-app.91898385.chunk.js +0 -36
  163. package/build/admin-edit-users.79eeb125.chunk.js +0 -10
  164. package/build/admin-users.123aa08e.chunk.js +0 -11
  165. package/build/api-tokens-list-page.505bf7e0.chunk.js +0 -16
  166. package/build/audit-logs-settings-page.4b422831.chunk.js +0 -1
  167. package/build/content-manager.2af15f57.chunk.js +0 -1099
  168. package/build/email-settings-page.d494d1eb.chunk.js +0 -11
  169. package/build/i18n-translation-ru-json.401bc498.chunk.js +0 -1
  170. package/build/main.f13fc96c.js +0 -2856
  171. package/build/review-workflows-settings-create-view.cb08cfa2.chunk.js +0 -1
  172. package/build/review-workflows-settings-edit-view.3c7cbe63.chunk.js +0 -1
  173. package/build/review-workflows-settings-list-view.1611dc1f.chunk.js +0 -56
  174. package/build/runtime~main.bb4efc54.js +0 -2
  175. package/build/transfer-tokens-list-page.22147d2c.chunk.js +0 -16
  176. package/build/users-advanced-settings-page.f0760eb8.chunk.js +0 -9
  177. package/build/users-email-settings-page.ff4b32f3.chunk.js +0 -9
  178. package/build/users-permissions-translation-zh-Hans-json.6ab714ee.chunk.js +0 -1
  179. package/build/users-providers-settings-page.48de0306.chunk.js +0 -14
  180. package/build/users-roles-settings-page.3f9f063e.chunk.js +0 -30
  181. package/build/webhook-edit-page.6cb479ff.chunk.js +0 -33
  182. package/build/zh-Hans-json.937b395b.chunk.js +0 -1
  183. package/ee/admin/hooks/index.js +0 -4
  184. package/ee/admin/hooks/useAuthProviders/index.js +0 -50
  185. package/ee/admin/hooks/useAuthProviders/reducer.js +0 -26
  186. package/ee/admin/hooks/useLicenseLimits/index.js +0 -1
  187. /package/ee/admin/hooks/{useLicenseLimits/__mocks__/index.js → __mocks__/useLicenseLimits.js} +0 -0
@@ -22,7 +22,7 @@ import {
22
22
  } from './exposedHooks';
23
23
  import favicon from './favicon.png';
24
24
  import injectionZones from './injectionZones';
25
- import App from './pages/App';
25
+ import { App } from './pages/App';
26
26
  import languageNativeNames from './translations/languageNativeNames';
27
27
 
28
28
  class StrapiApp {
@@ -0,0 +1,229 @@
1
+ import * as React from 'react';
2
+
3
+ import {
4
+ AppInfoProvider,
5
+ auth,
6
+ LoadingIndicatorPage,
7
+ useFetchClient,
8
+ useGuidedTour,
9
+ useNotification,
10
+ useStrapiApp,
11
+ } from '@strapi/helper-plugin';
12
+ import { useQueries } from 'react-query';
13
+ import { valid, lt } from 'semver';
14
+
15
+ import packageJSON from '../../../package.json';
16
+ import { useConfigurations } from '../hooks';
17
+ import { Admin } from '../pages/Admin';
18
+ import { getFullName } from '../utils/getFullName';
19
+ import { hashAdminUserEmail } from '../utils/uniqueAdminHash';
20
+
21
+ import RBACProvider from './RBACProvider';
22
+
23
+ const strapiVersion = packageJSON.version;
24
+
25
+ const checkLatestStrapiVersion = (currentPackageVersion, latestPublishedVersion) => {
26
+ if (!valid(currentPackageVersion) || !valid(latestPublishedVersion)) {
27
+ return false;
28
+ }
29
+
30
+ return lt(currentPackageVersion, latestPublishedVersion);
31
+ };
32
+
33
+ export const AuthenticatedApp = () => {
34
+ const { setGuidedTourVisibility } = useGuidedTour();
35
+ const toggleNotification = useNotification();
36
+ const userInfo = auth.getUserInfo();
37
+ const { get } = useFetchClient();
38
+ // TODO: replace with getDisplayName()
39
+ const [userDisplayName, setUserDisplayName] = React.useState(
40
+ userInfo?.userName ?? getFullName(userInfo.firstname, userInfo.lastname)
41
+ );
42
+ const [userId, setUserId] = React.useState(null);
43
+ const { showReleaseNotification } = useConfigurations();
44
+ const { plugins: appPlugins = {} } = useStrapiApp();
45
+ const [plugins, setPlugins] = React.useState(appPlugins);
46
+ const [
47
+ { data: appInfos, isLoading: isLoadingAppInfos },
48
+ { data: tagName },
49
+ { data: permissions, isLoading: isLoadingPermissions, refetch },
50
+ { data: userRoles },
51
+ ] = useQueries([
52
+ {
53
+ queryKey: 'information',
54
+ async queryFn() {
55
+ const {
56
+ data: { data },
57
+ } = await get('/admin/information');
58
+
59
+ return data;
60
+ },
61
+ },
62
+
63
+ {
64
+ queryKey: 'strapi-release',
65
+ async queryFn() {
66
+ const res = await fetch('https://api.github.com/repos/strapi/strapi/releases/latest');
67
+
68
+ if (!res.ok) {
69
+ throw new Error('Failed to fetch latest Strapi version.');
70
+ }
71
+
72
+ const { tag_name } = await res.json();
73
+
74
+ return tag_name;
75
+ },
76
+ enabled: showReleaseNotification,
77
+ initialData: strapiVersion,
78
+ onSuccess(data) {
79
+ const shouldUpdateStrapi = checkLatestStrapiVersion(strapiVersion, data.tag_name);
80
+
81
+ if (shouldUpdateStrapi && !JSON.parse(localStorage.getItem('STRAPI_UPDATE_NOTIF'))) {
82
+ toggleNotification({
83
+ type: 'info',
84
+ message: { id: 'notification.version.update.message' },
85
+ link: {
86
+ url: `https://github.com/strapi/strapi/releases/tag/${data.tag_name}`,
87
+ label: {
88
+ id: 'global.see-more',
89
+ },
90
+ },
91
+ blockTransition: true,
92
+ onClose: () => localStorage.setItem('STRAPI_UPDATE_NOTIF', true),
93
+ });
94
+ }
95
+ },
96
+ retry: false,
97
+ },
98
+ {
99
+ queryKey: ['users', 'me', 'permissions'],
100
+ async queryFn() {
101
+ const {
102
+ data: { data },
103
+ } = await get('/admin/users/me/permissions');
104
+
105
+ return data;
106
+ },
107
+ initialData: [],
108
+ },
109
+
110
+ {
111
+ queryKey: ['users', 'me'],
112
+ async queryFn() {
113
+ const {
114
+ data: {
115
+ data: { roles },
116
+ },
117
+ } = await get('/admin/users/me');
118
+
119
+ return roles;
120
+ },
121
+ },
122
+ ]);
123
+
124
+ // Display the guided tour conditionally for super admins in development mode
125
+ React.useEffect(() => {
126
+ if (userRoles) {
127
+ const isUserSuperAdmin = userRoles.find(({ code }) => code === 'strapi-super-admin');
128
+
129
+ if (isUserSuperAdmin && appInfos?.currentEnvironment === 'development') {
130
+ setGuidedTourVisibility(true);
131
+ }
132
+ }
133
+ }, [userRoles, appInfos, setGuidedTourVisibility]);
134
+
135
+ // Create a hash of the users email adress and use it as ID for tracking
136
+ React.useEffect(() => {
137
+ const generateUserId = async (userInfo) => {
138
+ const userId = await hashAdminUserEmail(userInfo);
139
+ setUserId(userId);
140
+ };
141
+
142
+ if (userInfo) {
143
+ generateUserId(userInfo);
144
+ }
145
+ }, [userInfo]);
146
+
147
+ /**
148
+ *
149
+ * I have spent some time trying to understand what is happening here, and wanted to
150
+ * leave that knowledge for my future me:
151
+ *
152
+ * `initializer` is an undocumented property of the `registerPlugin` API. At the time
153
+ * of writing it seems only to be used by the i18n plugin.
154
+ *
155
+ * How does it work?
156
+ *
157
+ * Every plugin that has an `initializer` component defined, receives the
158
+ * `setPlugin` function as a component prop. In the case of i18n the plugin fetches locales
159
+ * first and calls `setPlugin` with `pluginId` once they are loaded, which then triggers the
160
+ * reducer of the admin app defined above.
161
+ *
162
+ * Once all plugins are set to `isReady: true` the app renders.
163
+ *
164
+ * This API is used to block rendering of the admin app. We should remove that in v5 completely
165
+ * and make sure plugins can inject data into the global store before they are initialized, to avoid
166
+ * having a new prop-callback based communication channel between plugins and the core admin app.
167
+ *
168
+ */
169
+
170
+ const hasApluginNotReady = Object.values(plugins).some((plugin) => plugin.isReady === false);
171
+
172
+ if (
173
+ !userDisplayName ||
174
+ !userId ||
175
+ isLoadingAppInfos ||
176
+ isLoadingPermissions ||
177
+ hasApluginNotReady
178
+ ) {
179
+ const initializers = Object.keys(plugins).reduce((acc, current) => {
180
+ const InitializerComponent = plugins[current].initializer;
181
+
182
+ if (InitializerComponent) {
183
+ const key = plugins[current].pluginId;
184
+
185
+ acc.push(
186
+ <InitializerComponent
187
+ key={key}
188
+ setPlugin={(pluginId) => {
189
+ setPlugins((prev) => ({
190
+ ...prev,
191
+ [pluginId]: {
192
+ ...prev[pluginId],
193
+ isReady: true,
194
+ },
195
+ }));
196
+ }}
197
+ />
198
+ );
199
+ }
200
+
201
+ return acc;
202
+ }, []);
203
+
204
+ return (
205
+ <>
206
+ {initializers}
207
+ <LoadingIndicatorPage />
208
+ </>
209
+ );
210
+ }
211
+
212
+ return (
213
+ <AppInfoProvider
214
+ {...appInfos}
215
+ userId={userId}
216
+ latestStrapiReleaseTag={tagName}
217
+ // TODO: setUserDisplayName should not exist and be removed, as it is only used
218
+ // to update the displayName immediately, in case a user updates their profile.
219
+ // This information should be derived from the state.
220
+ setUserDisplayName={setUserDisplayName}
221
+ shouldUpdateStrapi={checkLatestStrapiVersion(strapiVersion, tagName)}
222
+ userDisplayName={userDisplayName}
223
+ >
224
+ <RBACProvider permissions={permissions} refetchPermissions={refetch}>
225
+ <Admin />
226
+ </RBACProvider>
227
+ </AppInfoProvider>
228
+ );
229
+ };
@@ -9,7 +9,7 @@ import Modal from './components/Modal';
9
9
  import StepperModal from './components/Stepper';
10
10
  import reducer, { initialState } from './reducer';
11
11
 
12
- const GuidedTourModal = () => {
12
+ export const GuidedTourModal = () => {
13
13
  const {
14
14
  currentStep,
15
15
  guidedTourState,
@@ -90,5 +90,3 @@ const GuidedTourModal = () => {
90
90
 
91
91
  return null;
92
92
  };
93
-
94
- export default GuidedTourModal;
@@ -64,22 +64,16 @@ const WysiwygNav = ({
64
64
  </Select>
65
65
 
66
66
  <MainButtons>
67
- <CustomIconButton disabled id="Bold" label="Bold" name="Bold" icon={<Bold />} />
68
- <CustomIconButton disabled id="Italic" label="Italic" name="Italic" icon={<Italic />} />
69
- <CustomIconButton
70
- disabled
71
- id="Underline"
72
- label="Underline"
73
- name="Underline"
74
- icon={<Underline />}
75
- />
67
+ <CustomIconButton disabled label="Bold" name="Bold" icon={<Bold />} />
68
+ <CustomIconButton disabled label="Italic" name="Italic" icon={<Italic />} />
69
+ <CustomIconButton disabled label="Underline" name="Underline" icon={<Underline />} />
76
70
  </MainButtons>
77
71
 
78
- <MoreButton disabled id="more" label="More" icon={<More />} />
72
+ <MoreButton disabled label="More" icon={<More />} />
79
73
  </StyledFlex>
80
74
 
81
75
  {!isExpandMode && (
82
- <Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
76
+ <Button onClick={onTogglePreviewMode} variant="tertiary">
83
77
  {formatMessage({
84
78
  id: 'components.Wysiwyg.ToggleMode.markdown-mode',
85
79
  defaultMessage: 'Markdown mode',
@@ -110,21 +104,18 @@ const WysiwygNav = ({
110
104
  <MainButtons>
111
105
  <CustomIconButton
112
106
  onClick={() => onActionClick('Bold', editorRef)}
113
- id="Bold"
114
107
  label="Bold"
115
108
  name="Bold"
116
109
  icon={<Bold />}
117
110
  />
118
111
  <CustomIconButton
119
112
  onClick={() => onActionClick('Italic', editorRef)}
120
- id="Italic"
121
113
  label="Italic"
122
114
  name="Italic"
123
115
  icon={<Italic />}
124
116
  />
125
117
  <CustomIconButton
126
118
  onClick={() => onActionClick('Underline', editorRef)}
127
- id="Underline"
128
119
  label="Underline"
129
120
  name="Underline"
130
121
  icon={<Underline />}
@@ -134,37 +125,27 @@ const WysiwygNav = ({
134
125
  <MoreButton
135
126
  ref={buttonMoreRef}
136
127
  onClick={handleTogglePopover}
137
- id="more"
138
128
  label="More"
139
129
  icon={<More />}
140
130
  />
141
131
  {visiblePopover && (
142
- <Popover
143
- onDismiss={handleTogglePopover}
144
- centered
145
- source={buttonMoreRef}
146
- spacing={4}
147
- id="popover"
148
- >
132
+ <Popover onDismiss={handleTogglePopover} centered source={buttonMoreRef} spacing={4}>
149
133
  <Flex>
150
134
  <IconButtonGroupMargin>
151
135
  <CustomIconButton
152
136
  onClick={() => onActionClick('Strikethrough', editorRef, handleTogglePopover)}
153
- id="Strikethrough"
154
137
  label="Strikethrough"
155
138
  name="Strikethrough"
156
139
  icon={<StrikeThrough />}
157
140
  />
158
141
  <CustomIconButton
159
142
  onClick={() => onActionClick('BulletList', editorRef, handleTogglePopover)}
160
- id="BulletList"
161
143
  label="BulletList"
162
144
  name="BulletList"
163
145
  icon={<BulletList />}
164
146
  />
165
147
  <CustomIconButton
166
148
  onClick={() => onActionClick('NumberList', editorRef, handleTogglePopover)}
167
- id="NumberList"
168
149
  label="NumberList"
169
150
  name="NumberList"
170
151
  icon={<NumberList />}
@@ -173,7 +154,6 @@ const WysiwygNav = ({
173
154
  <IconButtonGroup>
174
155
  <CustomIconButton
175
156
  onClick={() => onActionClick('Code', editorRef, handleTogglePopover)}
176
- id="Code"
177
157
  label="Code"
178
158
  name="Code"
179
159
  icon={<Code />}
@@ -183,14 +163,12 @@ const WysiwygNav = ({
183
163
  handleTogglePopover();
184
164
  onToggleMediaLib();
185
165
  }}
186
- id="Image"
187
166
  label="Image"
188
167
  name="Image"
189
168
  icon={<Image />}
190
169
  />
191
170
  <CustomLinkIconButton
192
171
  onClick={() => onActionClick('Link', editorRef, handleTogglePopover)}
193
- id="Link"
194
172
  label="Link"
195
173
  name="Link"
196
174
  // eslint-disable-next-line jsx-a11y/anchor-is-valid
@@ -198,7 +176,6 @@ const WysiwygNav = ({
198
176
  />
199
177
  <CustomIconButton
200
178
  onClick={() => onActionClick('Quote', editorRef, handleTogglePopover)}
201
- id="Quote"
202
179
  label="Quote"
203
180
  name="Quote"
204
181
  icon={<Quote />}
@@ -210,7 +187,7 @@ const WysiwygNav = ({
210
187
  </StyledFlex>
211
188
 
212
189
  {onTogglePreviewMode && (
213
- <Button onClick={onTogglePreviewMode} variant="tertiary" id="preview">
190
+ <Button onClick={onTogglePreviewMode} variant="tertiary">
214
191
  {formatMessage({
215
192
  id: 'components.Wysiwyg.ToggleMode.preview-mode',
216
193
  defaultMessage: 'Preview mode',
@@ -6,9 +6,10 @@ import { resetPermissions, setPermissions } from './actions';
6
6
  import { selectCollectionTypePermissions, selectPermissions } from './selectors';
7
7
 
8
8
  const useSyncRbac = (query, collectionTypeUID, containerName = 'listView') => {
9
+ const dispatch = useDispatch();
10
+
9
11
  const collectionTypesRelatedPermissions = useSelector(selectCollectionTypePermissions);
10
12
  const permissions = useSelector(selectPermissions);
11
- const dispatch = useDispatch();
12
13
 
13
14
  const relatedPermissions = collectionTypesRelatedPermissions[collectionTypeUID];
14
15
 
@@ -24,7 +25,14 @@ const useSyncRbac = (query, collectionTypeUID, containerName = 'listView') => {
24
25
  return () => {};
25
26
  }, [relatedPermissions, dispatch, query, containerName]);
26
27
 
27
- return permissions;
28
+ // Check if the permissions are related to the current collectionTypeUID
29
+ const isPermissionMismatch =
30
+ permissions?.some((permission) => permission.subject !== collectionTypeUID) ?? true;
31
+
32
+ return {
33
+ isValid: permissions && !isPermissionMismatch,
34
+ permissions,
35
+ };
28
36
  };
29
37
 
30
38
  export default useSyncRbac;
@@ -61,7 +61,7 @@ function renderDraglayerItem({ type, item }) {
61
61
  }
62
62
  }
63
63
 
64
- const App = () => {
64
+ export const ContentManger = () => {
65
65
  const contentTypeMatch = useRouteMatch(`/content-manager/:kind/:uid`);
66
66
  const { status, collectionTypeLinks, singleTypeLinks, models, refetchData } =
67
67
  useContentManagerInitData();
@@ -123,6 +123,10 @@ const App = () => {
123
123
 
124
124
  return (
125
125
  <Layout sideNav={<LeftMenu />}>
126
+ <Helmet
127
+ title={formatMessage({ id: getTrad('plugin.name'), defaultMessage: 'Content Manager' })}
128
+ />
129
+
126
130
  <DragLayer renderItem={renderDraglayerItem} />
127
131
  <ModelsContext.Provider value={{ refetchData }}>
128
132
  <Switch>
@@ -149,18 +153,3 @@ const App = () => {
149
153
  </Layout>
150
154
  );
151
155
  };
152
-
153
- export { App };
154
-
155
- export default function () {
156
- const { formatMessage } = useIntl();
157
-
158
- return (
159
- <>
160
- <Helmet
161
- title={formatMessage({ id: getTrad('plugin.name'), defaultMessage: 'Content Manager' })}
162
- />
163
- <App />
164
- </>
165
- );
166
- }
@@ -12,7 +12,7 @@ import { useFetchContentTypeLayout } from '../../hooks';
12
12
  import { formatLayoutToApi } from '../../utils';
13
13
  import EditSettingsView from '../EditSettingsView';
14
14
  import EditViewLayoutManager from '../EditViewLayoutManager';
15
- import ListSettingsView from '../ListSettingsView';
15
+ import { ListSettingsView } from '../ListSettingsView';
16
16
  import ListViewLayout from '../ListViewLayoutManager';
17
17
 
18
18
  import ErrorFallback from './components/ErrorFallback';
@@ -5,7 +5,7 @@ import { useCMEditViewDataManager } from '@strapi/helper-plugin';
5
5
  import PropTypes from 'prop-types';
6
6
  import { useIntl } from 'react-intl';
7
7
 
8
- import { getFullName } from '../../../../utils';
8
+ import { getFullName } from '../../../../utils/getFullName';
9
9
  import { getTrad } from '../../../utils';
10
10
 
11
11
  import getUnits from './utils/getUnits';
@@ -16,7 +16,7 @@ const EditViewLayoutManager = ({ layout, ...rest }) => {
16
16
  const dispatch = useDispatch();
17
17
  const [{ query }] = useQueryParams();
18
18
  const { runHookWaterfall } = useStrapiApp();
19
- const permissions = useSyncRbac(query, rest.slug, 'editView');
19
+ const { permissions, isValid: isValidPermissions } = useSyncRbac(query, rest.slug, 'editView');
20
20
 
21
21
  useEffect(() => {
22
22
  // Allow the plugins to extend the edit view layout
@@ -29,7 +29,7 @@ const EditViewLayoutManager = ({ layout, ...rest }) => {
29
29
  };
30
30
  }, [layout, dispatch, query, runHookWaterfall]);
31
31
 
32
- if (!currentLayout || !permissions) {
32
+ if (!currentLayout || !isValidPermissions) {
33
33
  return <LoadingIndicatorPage />;
34
34
  }
35
35
 
@@ -1,4 +1,4 @@
1
- import React, { useContext, useReducer, useState } from 'react';
1
+ import * as React from 'react';
2
2
 
3
3
  import {
4
4
  Button,
@@ -9,13 +9,7 @@ import {
9
9
  Layout,
10
10
  Main,
11
11
  } from '@strapi/design-system';
12
- import {
13
- ConfirmDialog,
14
- Link,
15
- useFetchClient,
16
- useNotification,
17
- useTracking,
18
- } from '@strapi/helper-plugin';
12
+ import { Link, useFetchClient, useNotification, useTracking } from '@strapi/helper-plugin';
19
13
  import { ArrowLeft, Check } from '@strapi/icons';
20
14
  import isEqual from 'lodash/isEqual';
21
15
  import upperFirst from 'lodash/upperFirst';
@@ -34,16 +28,14 @@ import { SortDisplayedFields } from './components/SortDisplayedFields';
34
28
  import { EXCLUDED_SORT_ATTRIBUTE_TYPES } from './constants';
35
29
  import reducer, { initialState } from './reducer';
36
30
 
37
- const ListSettingsView = ({ layout, slug }) => {
31
+ export const ListSettingsView = ({ layout, slug }) => {
38
32
  const { put } = useFetchClient();
39
33
  const { formatMessage } = useIntl();
40
34
  const { trackUsage } = useTracking();
41
35
  const pluginsQueryParams = usePluginsQueryParams();
42
36
  const toggleNotification = useNotification();
43
- const { refetchData } = useContext(ModelsContext);
44
- const [showWarningSubmit, setWarningSubmit] = useState(false);
45
- const toggleWarningSubmit = () => setWarningSubmit((prevState) => !prevState);
46
- const [{ fieldToEdit, fieldForm, initialData, modifiedData }, dispatch] = useReducer(
37
+ const { refetchData } = React.useContext(ModelsContext);
38
+ const [{ fieldToEdit, fieldForm, initialData, modifiedData }, dispatch] = React.useReducer(
47
39
  reducer,
48
40
  initialState,
49
41
  () => ({
@@ -101,16 +93,6 @@ const ListSettingsView = ({ layout, slug }) => {
101
93
  }
102
94
  );
103
95
 
104
- const handleConfirm = async () => {
105
- const { layouts, settings, metadatas } = modifiedData;
106
-
107
- mutate({
108
- layouts,
109
- settings,
110
- metadatas,
111
- });
112
- };
113
-
114
96
  const handleAddField = (item) => {
115
97
  dispatch({
116
98
  type: 'ADD_FIELD',
@@ -134,9 +116,17 @@ const ListSettingsView = ({ layout, slug }) => {
134
116
  }
135
117
  };
136
118
 
137
- const handleSubmit = (e) => {
138
- e.preventDefault();
139
- toggleWarningSubmit();
119
+ const handleSubmit = (event) => {
120
+ event.preventDefault();
121
+
122
+ const { layouts, settings, metadatas } = modifiedData;
123
+
124
+ mutate({
125
+ layouts,
126
+ settings,
127
+ metadatas,
128
+ });
129
+
140
130
  trackUsage('willSaveContentTypeLayout');
141
131
  };
142
132
 
@@ -253,19 +243,6 @@ const ListSettingsView = ({ layout, slug }) => {
253
243
  />
254
244
  </Flex>
255
245
  </ContentLayout>
256
-
257
- <ConfirmDialog
258
- bodyText={{
259
- id: getTrad('popUpWarning.warning.updateAllSettings'),
260
- defaultMessage: 'This will modify all your settings',
261
- }}
262
- iconRightButton={<Check />}
263
- isConfirmButtonLoading={isSubmittingForm}
264
- isOpen={showWarningSubmit}
265
- onToggleDialog={toggleWarningSubmit}
266
- onConfirm={handleConfirm}
267
- variantRightButton="success-light"
268
- />
269
246
  </form>
270
247
 
271
248
  {isModalFormOpen && (
@@ -305,5 +282,3 @@ ListSettingsView.propTypes = {
305
282
  }).isRequired,
306
283
  slug: PropTypes.string.isRequired,
307
284
  };
308
-
309
- export default ListSettingsView;
@@ -32,7 +32,7 @@ import { useSelector } from 'react-redux';
32
32
  import { Link, useHistory } from 'react-router-dom';
33
33
  import styled from 'styled-components';
34
34
 
35
- import formatAPIError from '../../../../../../utils/formatAPIErrors';
35
+ import { formatAPIErrors } from '../../../../../../utils/formatAPIErrors';
36
36
  import { getTrad, createYupSchema } from '../../../../../utils';
37
37
  import { listViewDomain } from '../../../selectors';
38
38
  import { Body } from '../../Body';
@@ -301,7 +301,7 @@ const SelectedEntriesModalContent = ({
301
301
  onError(error) {
302
302
  toggleNotification({
303
303
  type: 'warning',
304
- message: formatAPIError(error),
304
+ message: formatAPIErrors(error),
305
305
  });
306
306
  },
307
307
  }
@@ -23,7 +23,7 @@ import { useIntl } from 'react-intl';
23
23
  import { Link, useHistory } from 'react-router-dom';
24
24
 
25
25
  import { useEnterprise } from '../../../../../hooks/useEnterprise';
26
- import { getFullName } from '../../../../../utils';
26
+ import { getFullName } from '../../../../../utils/getFullName';
27
27
  import { usePluginsQueryParams } from '../../../../hooks';
28
28
  import { getTrad } from '../../../../utils';
29
29
  import CellContent from '../CellContent';
@@ -180,6 +180,22 @@ function ListView({
180
180
  data: { results, pagination: paginationResult },
181
181
  } = await fetchClient.get(endPoint, options);
182
182
 
183
+ // If user enters a page number that doesn't exist, redirect him to the last page
184
+ if (paginationResult.page > paginationResult.pageCount && paginationResult.pageCount > 0) {
185
+ const query = {
186
+ ...params,
187
+ page: paginationResult.pageCount,
188
+ };
189
+
190
+ push({
191
+ pathname,
192
+ state: { from: pathname },
193
+ search: stringify(query),
194
+ });
195
+
196
+ return;
197
+ }
198
+
183
199
  notifyStatus(
184
200
  formatMessage(
185
201
  {
@@ -219,7 +235,17 @@ function ListView({
219
235
  });
220
236
  }
221
237
  },
222
- [formatMessage, getData, getDataSucceeded, notifyStatus, push, toggleNotification, fetchClient]
238
+ [
239
+ formatMessage,
240
+ getData,
241
+ getDataSucceeded,
242
+ notifyStatus,
243
+ push,
244
+ toggleNotification,
245
+ fetchClient,
246
+ params,
247
+ pathname,
248
+ ]
223
249
  );
224
250
 
225
251
  const handleConfirmDeleteAllData = React.useCallback(
@@ -14,7 +14,7 @@ const ListViewLayout = ({ layout, ...props }) => {
14
14
  const dispatch = useDispatch();
15
15
  const { replace } = useHistory();
16
16
  const [{ query, rawQuery }] = useQueryParams();
17
- const permissions = useSyncRbac(query, props.slug, 'listView');
17
+ const { permissions, isValid: isValidPermissions } = useSyncRbac(query, props.slug, 'listView');
18
18
  const redirectionLink = useFindRedirectionLink(props.slug);
19
19
 
20
20
  useEffect(() => {
@@ -33,7 +33,7 @@ const ListViewLayout = ({ layout, ...props }) => {
33
33
  };
34
34
  }, [dispatch]);
35
35
 
36
- if (!permissions) {
36
+ if (!isValidPermissions) {
37
37
  return null;
38
38
  }
39
39