@strapi/admin 4.10.0-beta.0 → 4.10.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 (229) hide show
  1. package/.eslintignore +4 -0
  2. package/.eslintrc.js +14 -0
  3. package/admin/src/components/LanguageProvider/index.js +1 -1
  4. package/admin/src/components/LocalesProvider/__mocks__/useLocalesProvider.js +7 -0
  5. package/admin/src/components/LocalesProvider/index.js +2 -3
  6. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +3 -6
  7. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +2 -0
  8. package/admin/src/content-manager/components/DynamicTable/index.js +11 -29
  9. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +12 -6
  10. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/findAllAndReplace.js +10 -3
  11. package/admin/src/content-manager/components/InputUID/endActionStyle.js +4 -13
  12. package/admin/src/content-manager/components/InputUID/index.js +95 -72
  13. package/admin/src/content-manager/components/RepeatableComponent/components/Component.js +8 -2
  14. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +4 -8
  15. package/admin/src/content-manager/pages/App/LeftMenu/index.js +56 -35
  16. package/admin/src/content-manager/pages/App/actions.js +19 -7
  17. package/admin/src/content-manager/pages/App/constants.js +3 -3
  18. package/admin/src/content-manager/pages/App/index.js +5 -4
  19. package/admin/src/content-manager/pages/App/reducer.js +7 -6
  20. package/admin/src/content-manager/pages/App/selectors.js +3 -0
  21. package/admin/src/content-manager/pages/App/{useModels.js → useContentManagerInitData.js} +29 -28
  22. package/admin/src/content-manager/pages/App/utils/generateModelsLinks.js +2 -2
  23. package/admin/src/content-manager/pages/App/utils/getContentTypeLinks.js +17 -15
  24. package/admin/src/content-manager/pages/EditSettingsView/components/ModalForm.js +6 -5
  25. package/admin/src/content-manager/pages/EditSettingsView/index.js +4 -0
  26. package/admin/src/content-manager/pages/EditSettingsView/reducer.js +12 -7
  27. package/admin/src/content-manager/pages/EditSettingsView/utils/layout.js +1 -30
  28. package/admin/src/content-manager/pages/EditView/DeleteLink/index.js +5 -11
  29. package/admin/src/content-manager/pages/EditView/index.js +2 -7
  30. package/admin/src/content-manager/pages/EditViewLayoutManager/reducer.js +4 -4
  31. package/admin/src/content-manager/pages/ListSettingsView/index.js +1 -0
  32. package/admin/src/hooks/useConfigurations/__mocks__/index.js +7 -0
  33. package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +7 -8
  34. package/admin/src/hooks/useFetchMarketplaceProviders/utils/api.js +5 -0
  35. package/admin/src/hooks/useFetchPermissionsLayout/index.js +24 -15
  36. package/admin/src/hooks/useFetchRole/index.js +37 -30
  37. package/admin/src/hooks/useModels/index.js +22 -12
  38. package/admin/src/hooks/useRegenerate/index.js +12 -7
  39. package/admin/src/hooks/useSettingsForm/index.js +14 -6
  40. package/admin/src/pages/App/index.js +19 -24
  41. package/admin/src/pages/AuthPage/components/Register/index.js +46 -38
  42. package/admin/src/pages/HomePage/SocialLinks.js +1 -1
  43. package/admin/src/pages/MarketplacePage/components/EmptyNpmPackageSearch/index.js +3 -3
  44. package/admin/src/pages/MarketplacePage/components/NpmPackagesGrid/index.js +42 -9
  45. package/admin/src/pages/MarketplacePage/components/NpmPackagesPagination/index.js +26 -0
  46. package/admin/src/pages/MarketplacePage/components/OfflineLayout/index.js +45 -0
  47. package/admin/src/pages/MarketplacePage/index.js +80 -175
  48. package/admin/src/pages/MarketplacePage/utils/useMarketplaceData.js +85 -0
  49. package/admin/src/pages/SettingsPage/components/Tokens/FormHead/index.js +4 -0
  50. package/admin/src/pages/SettingsPage/components/Tokens/Regenerate/index.js +5 -3
  51. package/admin/src/pages/SettingsPage/components/Tokens/TokenTypeSelect/index.js +7 -5
  52. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/components/LogoModalStepper/PendingLogoDialog.js +1 -3
  53. package/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js +6 -12
  54. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/index.js +6 -10
  55. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/components/RoleRow/index.js +12 -4
  56. package/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js +21 -2
  57. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/components/FormTransferTokenContainer/index.js +41 -0
  58. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/index.js +53 -9
  59. package/admin/src/pages/SettingsPage/pages/TransferTokens/EditView/utils/schema.js +1 -0
  60. package/admin/src/pages/SettingsPage/pages/TransferTokens/ListView/index.js +27 -5
  61. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/index.js +18 -33
  62. package/admin/src/pages/SettingsPage/pages/Webhooks/ListView/index.js +41 -41
  63. package/admin/src/translations/en.json +52 -50
  64. package/admin/src/translations/fr.json +89 -1
  65. package/admin/src/translations/ru.json +51 -19
  66. package/build/1387.84b454d3.chunk.js +1 -0
  67. package/build/1657.45231968.chunk.js +168 -0
  68. package/build/3081.bcf9a12f.chunk.js +108 -0
  69. package/build/462.8fff7f3b.chunk.js +71 -0
  70. package/build/4628.20631dd1.chunk.js +1 -0
  71. package/build/5542.b8240e3f.chunk.js +70 -0
  72. package/build/5563.905daa13.chunk.js +79 -0
  73. package/build/6404.68405699.chunk.js +100 -0
  74. package/build/7259.b7d00cea.chunk.js +1 -0
  75. package/build/8694.6522968d.chunk.js +247 -0
  76. package/build/Admin-authenticatedApp.b036fe95.chunk.js +79 -0
  77. package/build/Admin_InternalErrorPage.15c6bf07.chunk.js +1 -0
  78. package/build/Admin_homePage.f9309c6d.chunk.js +73 -0
  79. package/build/Admin_marketplace.56bc1008.chunk.js +31 -0
  80. package/build/Admin_pluginsPage.f6b52ee9.chunk.js +6 -0
  81. package/build/Admin_profilePage.9112cffc.chunk.js +15 -0
  82. package/build/Admin_settingsPage.4604a16c.chunk.js +79 -0
  83. package/build/Upload_ConfigureTheView.eaaec495.chunk.js +1 -0
  84. package/build/admin-app.014adc27.chunk.js +110 -0
  85. package/build/admin-edit-roles-page.8a4063f7.chunk.js +280 -0
  86. package/build/admin-edit-users.7e14d85f.chunk.js +10 -0
  87. package/build/admin-roles-list.329c1f63.chunk.js +31 -0
  88. package/build/admin-users.d02de059.chunk.js +34 -0
  89. package/build/api-tokens-create-page.97595e12.chunk.js +1 -0
  90. package/build/api-tokens-edit-page.cd36e30e.chunk.js +1 -0
  91. package/build/api-tokens-list-page.6757c7b9.chunk.js +16 -0
  92. package/build/audit-logs-settings-page.19d90bda.chunk.js +76 -0
  93. package/build/content-manager.84f81966.chunk.js +1130 -0
  94. package/build/content-type-builder-list-view.9c2c020c.chunk.js +214 -0
  95. package/build/{content-type-builder-translation-de-json.0979cccb.chunk.js → content-type-builder-translation-de-json.393a76c0.chunk.js} +1 -1
  96. package/build/{content-type-builder-translation-dk-json.e05583e9.chunk.js → content-type-builder-translation-dk-json.fbd39bb7.chunk.js} +1 -1
  97. package/build/content-type-builder-translation-en-json.446b611d.chunk.js +1 -0
  98. package/build/{content-type-builder-translation-es-json.fe4daad8.chunk.js → content-type-builder-translation-es-json.9288474b.chunk.js} +1 -1
  99. package/build/{content-type-builder-translation-fr-json.b1eb52f6.chunk.js → content-type-builder-translation-fr-json.d35e269c.chunk.js} +1 -1
  100. package/build/{content-type-builder-translation-id-json.ee3b36bb.chunk.js → content-type-builder-translation-id-json.f0513929.chunk.js} +1 -1
  101. package/build/{content-type-builder-translation-it-json.13b3c26a.chunk.js → content-type-builder-translation-it-json.aaf16753.chunk.js} +1 -1
  102. package/build/{content-type-builder-translation-ko-json.8a274be5.chunk.js → content-type-builder-translation-ko-json.8fe21a7f.chunk.js} +1 -1
  103. package/build/{content-type-builder-translation-ms-json.2d29c1e0.chunk.js → content-type-builder-translation-ms-json.3b5d2d3e.chunk.js} +1 -1
  104. package/build/{content-type-builder-translation-nl-json.40bbc562.chunk.js → content-type-builder-translation-nl-json.225ef5d3.chunk.js} +1 -1
  105. package/build/{content-type-builder-translation-pl-json.24a34349.chunk.js → content-type-builder-translation-pl-json.92f36be2.chunk.js} +1 -1
  106. package/build/{content-type-builder-translation-pt-BR-json.97f71a9d.chunk.js → content-type-builder-translation-pt-BR-json.3bd10f89.chunk.js} +1 -1
  107. package/build/{content-type-builder-translation-ru-json.54d11230.chunk.js → content-type-builder-translation-ru-json.9bfe47ce.chunk.js} +1 -1
  108. package/build/{content-type-builder-translation-sk-json.626c9493.chunk.js → content-type-builder-translation-sk-json.d03cc18a.chunk.js} +1 -1
  109. package/build/{content-type-builder-translation-sv-json.59f5e1e5.chunk.js → content-type-builder-translation-sv-json.d23dcd32.chunk.js} +1 -1
  110. package/build/{content-type-builder-translation-th-json.6fe3ed55.chunk.js → content-type-builder-translation-th-json.7ad256e2.chunk.js} +1 -1
  111. package/build/{content-type-builder-translation-tr-json.cea4d226.chunk.js → content-type-builder-translation-tr-json.926f6191.chunk.js} +1 -1
  112. package/build/{content-type-builder-translation-uk-json.c4524247.chunk.js → content-type-builder-translation-uk-json.7bf19546.chunk.js} +1 -1
  113. package/build/{content-type-builder-translation-zh-json.faedd610.chunk.js → content-type-builder-translation-zh-json.ad24dbeb.chunk.js} +1 -1
  114. package/build/content-type-builder.68af11d2.chunk.js +126 -0
  115. package/build/email-settings-page.1095e1ab.chunk.js +10 -0
  116. package/build/en-json.c7fc79af.chunk.js +1 -0
  117. package/build/fr-json.5947cf63.chunk.js +1 -0
  118. package/build/{highlight.js.26ef649f.chunk.js → highlight.js.28a1547e.chunk.js} +2 -2
  119. package/build/i18n-settings-page.d95b32df.chunk.js +60 -0
  120. package/build/index.html +1 -1
  121. package/build/main.841e0dcb.js +2280 -0
  122. package/build/review-workflows-settings.f7890c40.chunk.js +106 -0
  123. package/build/ru-json.e0662702.chunk.js +1 -0
  124. package/build/{runtime~main.5a95bee6.js → runtime~main.965f8af8.js} +1 -1
  125. package/build/sso-settings-page.1dd4886e.chunk.js +1 -0
  126. package/build/transfer-tokens-create-page.ec2ca215.chunk.js +1 -0
  127. package/build/transfer-tokens-edit-page.22bf28e5.chunk.js +1 -0
  128. package/build/transfer-tokens-list-page.cf8c77f2.chunk.js +16 -0
  129. package/build/upload-settings.945fdcfa.chunk.js +13 -0
  130. package/build/{upload-translation-th-json.3847dae0.chunk.js → upload-translation-th-json.98d35574.chunk.js} +1 -1
  131. package/build/upload.a86b1054.chunk.js +33 -0
  132. package/build/users-advanced-settings-page.5b5a9baa.chunk.js +8 -0
  133. package/build/users-email-settings-page.e5506eb4.chunk.js +23 -0
  134. package/build/users-providers-settings-page.e32089c2.chunk.js +28 -0
  135. package/build/users-roles-settings-page.20656f92.chunk.js +30 -0
  136. package/build/webhook-edit-page.a3b62049.chunk.js +75 -0
  137. package/build/webhook-list-page.ca38eeef.chunk.js +71 -0
  138. package/{admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/ReviewWorkflowsStage.js → ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/ReviewWorkflowsStageEE.js} +2 -2
  139. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +47 -0
  140. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js +3 -0
  141. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +64 -18
  142. package/ee/admin/hooks/useAuthProviders/index.js +28 -29
  143. package/ee/admin/hooks/useLicenseLimitNotification/index.js +1 -1
  144. package/ee/admin/pages/AuthResponse/index.js +7 -3
  145. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/hooks/useAuditLogsData.js +6 -3
  146. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/index.js +15 -5
  147. package/ee/admin/pages/SettingsPage/pages/AuditLogs/ListView/utils/getDisplayedFilters.js +52 -45
  148. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/ReviewWorkflows.js +9 -5
  149. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +2 -2
  150. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +2 -2
  151. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +2 -1
  152. package/ee/server/content-types/workflow/index.js +0 -3
  153. package/ee/server/content-types/workflow-stage/index.js +0 -5
  154. package/ee/server/controllers/authentication/middlewares.js +2 -1
  155. package/ee/server/controllers/workflows/stages/index.js +8 -1
  156. package/ee/server/register.js +3 -1
  157. package/ee/server/services/audit-logs.js +75 -16
  158. package/ee/server/services/index.js +1 -0
  159. package/ee/server/services/review-workflows/entity-service-decorator.js +17 -5
  160. package/ee/server/services/review-workflows/metrics.js +24 -0
  161. package/ee/server/services/review-workflows/review-workflows.js +44 -94
  162. package/ee/server/services/review-workflows/stages.js +136 -17
  163. package/ee/server/utils/persisted-tables.js +116 -22
  164. package/ee/server/utils/review-workflows.js +9 -0
  165. package/jest.config.front.js +1 -6
  166. package/package.json +25 -23
  167. package/server/controllers/transfer/runner.js +4 -2
  168. package/server/middlewares/data-transfer.js +4 -1
  169. package/server/routes/transfer.js +13 -4
  170. package/server/services/constants.js +4 -0
  171. package/server/services/transfer/permission.js +1 -1
  172. package/server/services/transfer/token.js +33 -31
  173. package/server/strategies/api-token.js +8 -1
  174. package/server/validation/transfer/token.js +10 -2
  175. package/webpack.config.js +6 -2
  176. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js +0 -1
  177. package/admin/src/content-manager/pages/App/LeftMenu/utils/index.js +0 -1
  178. package/admin/src/content-manager/pages/App/LeftMenu/utils/matchByTitle.js +0 -24
  179. package/build/2263.4c5916f9.chunk.js +0 -98
  180. package/build/4049.64715f20.chunk.js +0 -1
  181. package/build/4649.213b8a3b.chunk.js +0 -30
  182. package/build/6985.66cca29c.chunk.js +0 -1
  183. package/build/7259.aefb51e8.chunk.js +0 -1
  184. package/build/8469.853c822b.chunk.js +0 -1
  185. package/build/9505.dbe702ab.chunk.js +0 -14
  186. package/build/9816.01ee964f.chunk.js +0 -2
  187. package/build/Admin-authenticatedApp.f50ad423.chunk.js +0 -79
  188. package/build/Admin_InternalErrorPage.4ad8b0df.chunk.js +0 -1
  189. package/build/Admin_homePage.1411fb7c.chunk.js +0 -68
  190. package/build/Admin_marketplace.02608d56.chunk.js +0 -22
  191. package/build/Admin_pluginsPage.15e3b0fd.chunk.js +0 -1
  192. package/build/Admin_profilePage.76afeca0.chunk.js +0 -15
  193. package/build/Admin_settingsPage.147755cd.chunk.js +0 -9
  194. package/build/Upload_ConfigureTheView.34dde278.chunk.js +0 -1
  195. package/build/admin-app.55dd7921.chunk.js +0 -112
  196. package/build/admin-edit-roles-page.cf543488.chunk.js +0 -216
  197. package/build/admin-edit-users.31c20712.chunk.js +0 -10
  198. package/build/admin-roles-list.489c501f.chunk.js +0 -2
  199. package/build/admin-users.3e111a7d.chunk.js +0 -11
  200. package/build/api-tokens-create-page.4328b852.chunk.js +0 -1
  201. package/build/api-tokens-edit-page.bce5050f.chunk.js +0 -1
  202. package/build/api-tokens-list-page.93f24348.chunk.js +0 -16
  203. package/build/audit-logs-settings-page.7be97e82.chunk.js +0 -1
  204. package/build/content-manager.4480ae88.chunk.js +0 -1137
  205. package/build/content-type-builder-list-view.cf38fe2f.chunk.js +0 -191
  206. package/build/content-type-builder-translation-en-json.7961593e.chunk.js +0 -1
  207. package/build/content-type-builder.af9abf1e.chunk.js +0 -126
  208. package/build/email-settings-page.4bdbef9a.chunk.js +0 -3
  209. package/build/en-json.697b4bcf.chunk.js +0 -1
  210. package/build/fr-json.f66c3211.chunk.js +0 -1
  211. package/build/i18n-settings-page.2bb5be96.chunk.js +0 -1
  212. package/build/main.af8c0f31.js +0 -3790
  213. package/build/review-workflows-settings.7a7dc773.chunk.js +0 -57
  214. package/build/ru-json.6a01cea6.chunk.js +0 -1
  215. package/build/sso-settings-page.272b87c8.chunk.js +0 -1
  216. package/build/transfer-tokens-create-page.a1f14bb1.chunk.js +0 -1
  217. package/build/transfer-tokens-edit-page.00ee1c74.chunk.js +0 -1
  218. package/build/transfer-tokens-list-page.ce37354b.chunk.js +0 -16
  219. package/build/upload-settings.0875e973.chunk.js +0 -1
  220. package/build/upload.c7da1611.chunk.js +0 -13
  221. package/build/users-advanced-settings-page.1d3c14c7.chunk.js +0 -1
  222. package/build/users-email-settings-page.e8db68c4.chunk.js +0 -1
  223. package/build/users-providers-settings-page.14cac425.chunk.js +0 -1
  224. package/build/users-roles-settings-page.2ea4de84.chunk.js +0 -30
  225. package/build/webhook-edit-page.329141a5.chunk.js +0 -23
  226. package/build/webhook-list-page.029957a4.chunk.js +0 -1
  227. package/ee/server/migrations/review-workflows.js +0 -39
  228. package/ee/server/utils/test.js +0 -11
  229. /package/admin/src/{content-manager/components/InputUID/useDebounce.js → hooks/useDebounce/index.js} +0 -0
@@ -4,12 +4,15 @@ const {
4
4
  mapAsync,
5
5
  errors: { ApplicationError },
6
6
  } = require('@strapi/utils');
7
+ const { map } = require('lodash/fp');
7
8
 
8
9
  const { STAGE_MODEL_UID, ENTITY_STAGE_ATTRIBUTE } = require('../../constants/workflows');
9
10
  const { getService } = require('../../utils');
11
+ const { getContentTypeUIDsWithActivatedReviewWorkflows } = require('../../utils/review-workflows');
10
12
 
11
13
  module.exports = ({ strapi }) => {
12
14
  const workflowsService = getService('workflows', { strapi });
15
+ const metrics = getService('review-workflows-metrics', { strapi });
13
16
 
14
17
  return {
15
18
  find({ workflowId, populate }) {
@@ -20,31 +23,43 @@ module.exports = ({ strapi }) => {
20
23
  return strapi.entityService.findMany(STAGE_MODEL_UID, params);
21
24
  },
22
25
 
23
- findById(id, { workflowId, populate }) {
26
+ findById(id, { populate } = {}) {
24
27
  const params = {
25
- filters: { workflow: workflowId },
26
28
  populate,
27
29
  };
28
30
  return strapi.entityService.findOne(STAGE_MODEL_UID, id, params);
29
31
  },
30
32
 
31
- createMany(stagesList, { fields }) {
32
- const params = {
33
- select: fields,
34
- };
35
- return Promise.all(
33
+ async createMany(stagesList, { fields }) {
34
+ const params = { select: fields };
35
+
36
+ const stages = await Promise.all(
36
37
  stagesList.map((stage) =>
37
38
  strapi.entityService.create(STAGE_MODEL_UID, { data: stage, ...params })
38
39
  )
39
40
  );
41
+
42
+ metrics.sendDidCreateStage();
43
+
44
+ return stages;
40
45
  },
41
46
 
42
- update(stageId, stageData) {
43
- return strapi.entityService.update(STAGE_MODEL_UID, stageId, { data: stageData });
47
+ async update(stageId, stageData) {
48
+ const stage = await strapi.entityService.update(STAGE_MODEL_UID, stageId, {
49
+ data: stageData,
50
+ });
51
+
52
+ metrics.sendDidEditStage();
53
+
54
+ return stage;
44
55
  },
45
56
 
46
- delete(stageId) {
47
- return strapi.entityService.delete(STAGE_MODEL_UID, stageId);
57
+ async delete(stageId) {
58
+ const stage = await strapi.entityService.delete(STAGE_MODEL_UID, stageId);
59
+
60
+ metrics.sendDidDeleteStage();
61
+
62
+ return stage;
48
63
  },
49
64
 
50
65
  count() {
@@ -58,12 +73,41 @@ module.exports = ({ strapi }) => {
58
73
 
59
74
  assertAtLeastOneStageRemain(workflow.stages, { created, deleted });
60
75
 
61
- return strapi.db.transaction(async () => {
62
- const newStages = await this.createMany(created, { fields: ['id'] });
63
- const stagesIds = stages.map((stage) => stage.id ?? newStages.shift().id);
76
+ return strapi.db.transaction(async ({ trx }) => {
77
+ // Create the new stages
78
+ const createdStages = await this.createMany(created, { fields: ['id'] });
79
+ // Put all the newly created stages ids
80
+ const createdStagesIds = map('id', createdStages);
81
+ const stagesIds = stages.map((stage) => stage.id ?? createdStagesIds.shift());
82
+ const contentTypes = getContentTypeUIDsWithActivatedReviewWorkflows(strapi.contentTypes);
64
83
 
84
+ // Update the workflow stages
65
85
  await mapAsync(updated, (stage) => this.update(stage.id, stage));
66
- await mapAsync(deleted, (stage) => this.delete(stage.id));
86
+
87
+ // Delete the stages that are not in the new stages list
88
+ await mapAsync(deleted, async (stage) => {
89
+ // Find the nearest stage in the workflow and newly created stages
90
+ // that is not deleted, prioritizing the previous stages
91
+ const nearestStage = findNearestMatchingStage(
92
+ [...workflow.stages, ...createdStages],
93
+ workflow.stages.findIndex((s) => s.id === stage.id),
94
+ (targetStage) => {
95
+ return !deleted.find((s) => s.id === targetStage.id);
96
+ }
97
+ );
98
+
99
+ // Assign the new stage to entities that had the deleted stage
100
+ await mapAsync(contentTypes, (contentTypeUID) => {
101
+ this.updateEntitiesStage(contentTypeUID, {
102
+ fromStageId: stage.id,
103
+ toStageId: nearestStage.id,
104
+ trx,
105
+ });
106
+ });
107
+
108
+ return this.delete(stage.id);
109
+ });
110
+
67
111
  return workflowsService.update(workflowId, {
68
112
  stages: stagesIds,
69
113
  });
@@ -78,11 +122,61 @@ module.exports = ({ strapi }) => {
78
122
  * @param {string} entityInfo.modelUID - the content-type of the entity
79
123
  * @param {number} stageId - The id of the stage to assign to the entity
80
124
  */
81
- updateEntity(entityInfo, stageId) {
82
- return strapi.entityService.update(entityInfo.modelUID, entityInfo.id, {
125
+ async updateEntity(entityInfo, stageId) {
126
+ const stage = await this.findById(stageId);
127
+
128
+ if (!stage) {
129
+ throw new ApplicationError(`Selected stage does not exist`);
130
+ }
131
+
132
+ const entity = await strapi.entityService.update(entityInfo.modelUID, entityInfo.id, {
83
133
  data: { [ENTITY_STAGE_ATTRIBUTE]: stageId },
84
134
  populate: [ENTITY_STAGE_ATTRIBUTE],
85
135
  });
136
+
137
+ metrics.sendDidChangeEntryStage();
138
+
139
+ return entity;
140
+ },
141
+
142
+ /**
143
+ * Updates the stage of all entities of a content type that are in a specific stage
144
+ * @param {string} contentTypeUID
145
+ * @param {number} fromStageId
146
+ * @param {number} toStageId
147
+ * @param {KnexTransaction} trx
148
+ * @returns
149
+ */
150
+ async updateEntitiesStage(contentTypeUID, { fromStageId, toStageId, trx = null }) {
151
+ const { attributes, tableName } = strapi.db.metadata.get(contentTypeUID);
152
+ const joinTable = attributes[ENTITY_STAGE_ATTRIBUTE].joinTable;
153
+ const joinColumn = joinTable.joinColumn.name;
154
+ const invJoinColumn = joinTable.inverseJoinColumn.name;
155
+
156
+ const selectStatement = strapi.db
157
+ .getConnection()
158
+ .select({ [joinColumn]: 't1.id', [invJoinColumn]: toStageId })
159
+ .from(`${tableName} as t1`)
160
+ .leftJoin(`${joinTable.name} as t2`, `t1.id`, `t2.${joinColumn}`)
161
+ .where(`t2.${invJoinColumn}`, fromStageId)
162
+ .toSQL();
163
+
164
+ // Insert rows for all entries of the content type that do not have a
165
+ // default stage
166
+ const query = strapi.db
167
+ .getConnection(joinTable.name)
168
+ .insert(
169
+ strapi.db.connection.raw(
170
+ `(${joinColumn}, ${invJoinColumn}) ${selectStatement.sql}`,
171
+ selectStatement.bindings
172
+ )
173
+ );
174
+
175
+ if (trx) {
176
+ query.transacting(trx);
177
+ }
178
+
179
+ return query;
86
180
  },
87
181
  };
88
182
  };
@@ -146,3 +240,28 @@ function assertAtLeastOneStageRemain(workflowStages, diffStages) {
146
240
  throw new ApplicationError('At least one stage must remain in the workflow.');
147
241
  }
148
242
  }
243
+
244
+ /**
245
+ * Find the id of the nearest object in an array that matches a condition.
246
+ * Used for searching for the nearest stage that is not deleted.
247
+ * Starts by searching the elements before the index, then the remaining elements in the array.
248
+ *
249
+ * @param {Array} stages
250
+ * @param {Number} startIndex the index to start searching from
251
+ * @param {Function} condition must evaluate to true for the object to be considered a match
252
+ * @returns {Object} stage
253
+ */
254
+ function findNearestMatchingStage(stages, startIndex, condition) {
255
+ // Start by searching the elements before the startIndex
256
+ for (let i = startIndex; i >= 0; i -= 1) {
257
+ if (condition(stages[i])) {
258
+ return stages[i];
259
+ }
260
+ }
261
+
262
+ // If no matching element is found before the startIndex,
263
+ // search the remaining elements in the array
264
+ const remainingArray = stages.slice(startIndex + 1);
265
+ const nearestObject = remainingArray.filter(condition)[0];
266
+ return nearestObject;
267
+ }
@@ -1,49 +1,143 @@
1
1
  'use strict';
2
2
 
3
+ const { differenceWith, isEqual } = require('lodash/fp');
4
+
3
5
  /**
4
- * Finds all tables in the database that start with a prefix
5
- * @param {string} prefix
6
- * @returns {Array}
6
+ * Transform table name to the object format
7
+ * @param {Array<string|{ table: string; dependsOn?: Array<{ table: string;}> }>} table
8
+ * @returns Array<{ table: string; dependsOn?: Array<{ table: string;}> }>
7
9
  */
8
- const findTablesThatStartWithPrefix = async (prefix) => {
9
- const tables = await strapi.db.dialect.schemaInspector.getTables();
10
- return tables.filter((tableName) => tableName.startsWith(prefix));
10
+ const transformTableName = (table) => {
11
+ if (typeof table === 'string') {
12
+ return { name: table };
13
+ }
14
+ return table;
11
15
  };
12
16
 
13
17
  /**
14
- * Get all reserved table names from the core store
15
- * @returns {Array}
18
+ * Finds all tables in the database matching the regular expression
19
+ * @param {Object} ctx
20
+ * @param {Strapi} ctx.strapi
21
+ * @param {RegExp} regex
22
+ * @returns {Promise<string[]>}
23
+ */
24
+ async function findTables({ strapi }, regex) {
25
+ const tables = await strapi.db.dialect.schemaInspector.getTables();
26
+ return tables.filter((tableName) => regex.test(tableName));
27
+ }
28
+
29
+ /**
30
+ * Add tables name to the reserved tables in core store
31
+ * @param {Object} ctx
32
+ * @param {Strapi} ctx.strapi
33
+ * @param {Array<string|{ table: string; dependsOn?: Array<{ table: string;}> }>} tableNames
34
+ * @return {Promise<void>}
16
35
  */
17
- const getPersistedTables = async () =>
18
- (await strapi.store.get({
36
+ async function addPersistTables({ strapi }, tableNames) {
37
+ const persistedTables = await getPersistedTables({ strapi });
38
+ const tables = tableNames.map(transformTableName);
39
+
40
+ // Get new tables to be persisted, remove tables if they already were persisted
41
+ const notPersistedTableNames = differenceWith(isEqual, tables, persistedTables);
42
+ // Remove tables that are going to be changed
43
+ const tablesToPersist = differenceWith(
44
+ (t1, t2) => t1.name === t2.name,
45
+ persistedTables,
46
+ notPersistedTableNames
47
+ );
48
+
49
+ if (!notPersistedTableNames.length) {
50
+ return;
51
+ }
52
+
53
+ tablesToPersist.push(...notPersistedTableNames);
54
+ await strapi.store.set({
19
55
  type: 'core',
20
56
  key: 'persisted_tables',
21
- })) ?? [];
57
+ value: tablesToPersist,
58
+ });
59
+ }
22
60
 
23
61
  /**
24
- * Add all table names that start with a prefix to the reserved tables in
25
- * core store
26
- * @param {string} tableNamePrefix
62
+ * Remove tables name from the reserved tables in core store
63
+ * @param {Object} ctx
64
+ * @param {Strapi} ctx.strapi
65
+ * @param {Array<string>} tableNames
66
+ * @return {Promise<void>}
27
67
  */
68
+ async function removePersistedTables({ strapi }, tableNames) {
69
+ const persistedTables = await getPersistedTables({ strapi });
28
70
 
29
- const persistTablesWithPrefix = async (tableNamePrefix) => {
30
- const persistedTables = await getPersistedTables();
31
- const tableNames = await findTablesThatStartWithPrefix(tableNamePrefix);
32
- const notReservedTableNames = tableNames.filter((name) => !persistedTables.includes(name));
71
+ // Get new tables to be persisted, remove tables if they already were persisted
72
+ const newPersistedTables = differenceWith(
73
+ (t1, t2) => t1.name === t2,
74
+ persistedTables,
75
+ tableNames
76
+ );
33
77
 
34
- if (!notReservedTableNames.length) {
78
+ if (newPersistedTables.length === persistedTables.length) {
35
79
  return;
36
80
  }
37
81
 
38
- persistedTables.push(...notReservedTableNames);
39
82
  await strapi.store.set({
40
83
  type: 'core',
41
84
  key: 'persisted_tables',
42
- value: persistedTables,
85
+ value: newPersistedTables,
43
86
  });
87
+ }
88
+
89
+ /**
90
+ * Get all reserved table names from the core store
91
+ * @param {Object} ctx
92
+ * @param {Strapi} ctx.strapi
93
+ * @returns {Promise<string[]>}
94
+ */
95
+
96
+ async function getPersistedTables({ strapi }) {
97
+ const persistedTables = await strapi.store.get({
98
+ type: 'core',
99
+ key: 'persisted_tables',
100
+ });
101
+
102
+ return (persistedTables || []).map(transformTableName);
103
+ }
104
+
105
+ /**
106
+ * Add all table names that start with a prefix to the reserved tables in
107
+ * core store
108
+ * @param {string} tableNamePrefix
109
+ * @return {Promise<void>}
110
+ */
111
+
112
+ const persistTablesWithPrefix = async (tableNamePrefix) => {
113
+ const tableNameRegex = new RegExp(`^${tableNamePrefix}.*`);
114
+ const tableNames = await findTables({ strapi }, tableNameRegex);
115
+
116
+ await addPersistTables({ strapi }, tableNames);
117
+ };
118
+
119
+ /**
120
+ * Remove all table names that end with a suffix from the reserved tables in core store
121
+ * @param {string} tableNameSuffix
122
+ * @return {Promise<void>}
123
+ */
124
+ const removePersistedTablesWithSuffix = async (tableNameSuffix) => {
125
+ const tableNameRegex = new RegExp(`.*${tableNameSuffix}$`);
126
+ const tableNames = await findTables({ strapi }, tableNameRegex);
127
+ await removePersistedTables({ strapi }, tableNames);
128
+ };
129
+
130
+ /**
131
+ * Add tables to the reserved tables in core store
132
+ * @param {Array<string|{ table: string; dependsOn?: Array<{ table: string;}> }} tables
133
+ */
134
+ const persistTables = async (tables) => {
135
+ await addPersistTables({ strapi }, tables);
44
136
  };
45
137
 
46
138
  module.exports = {
47
139
  persistTablesWithPrefix,
48
- findTablesThatStartWithPrefix,
140
+ removePersistedTablesWithSuffix,
141
+ persistTables,
142
+ findTables,
49
143
  };
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const { get, keys, pickBy, pipe } = require('lodash/fp');
3
4
  const { WORKFLOW_MODEL_UID } = require('../constants/workflows');
4
5
 
5
6
  /**
@@ -19,7 +20,15 @@ function hasReviewWorkflow({ strapi }, contentType) {
19
20
  const getDefaultWorkflow = async ({ strapi }) =>
20
21
  strapi.query(WORKFLOW_MODEL_UID).findOne({ populate: ['stages'] });
21
22
 
23
+ const getContentTypeUIDsWithActivatedReviewWorkflows = pipe([
24
+ // Pick only content-types with reviewWorkflows options set to true
25
+ pickBy(get('options.reviewWorkflows')),
26
+ // Get UIDs
27
+ keys,
28
+ ]);
29
+
22
30
  module.exports = {
23
31
  hasReviewWorkflow,
24
32
  getDefaultWorkflow,
33
+ getContentTypeUIDsWithActivatedReviewWorkflows,
25
34
  };
@@ -1,11 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const baseConfig = require('../../../jest.base-config.front');
4
- const pkg = require('./package.json');
5
-
6
3
  module.exports = {
7
- ...baseConfig,
8
- displayName: (pkg.strapi && pkg.strapi.name) || pkg.name,
9
- roots: [__dirname],
4
+ preset: '../../../jest-preset.front.js',
10
5
  collectCoverageFrom: ['<rootDir>/packages/core/admin/admin/**/*.js'],
11
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/admin",
3
- "version": "4.10.0-beta.0",
3
+ "version": "4.10.0",
4
4
  "description": "Strapi Admin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -28,14 +28,16 @@
28
28
  "develop:webpack": "cross-env NODE_ENV=development webpack serve --config webpack.config.dev.js --progress profile",
29
29
  "prepublishOnly": "yarn build",
30
30
  "build": "rimraf build && node ./scripts/build.js",
31
- "build:mesure": "rimraf build && cross-env MESURE_BUILD_SPEED=true node ./scripts/build.js",
31
+ "build:measure": "rimraf build && cross-env MEASURE_BUILD_SPEED=true node ./scripts/build.js",
32
32
  "test": "echo \"no tests yet\"",
33
- "test:unit": "jest --verbose",
34
- "test:front": "cross-env IS_EE=true jest --config ./jest.config.front.js",
35
- "test:front:watch": "cross-env IS_EE=true jest --config ./jest.config.front.js --watchAll",
36
- "test:front:ce": "cross-env IS_EE=false jest --config ./jest.config.front.js",
37
- "test:front:watch:ce": "cross-env IS_EE=false jest --config ./jest.config.front.js --watchAll",
38
- "test:front:ce:cov": "cross-env IS_EE=false jest --config ./jest.config.front.js --coverage"
33
+ "test:unit": "run -T jest",
34
+ "test:unit:watch": "run -T jest --watch",
35
+ "test:front": "run -T cross-env IS_EE=true jest --config ./jest.config.front.js",
36
+ "test:front:watch": "run -T cross-env IS_EE=true jest --config ./jest.config.front.js --watchAll",
37
+ "test:front:ce": "run -T cross-env IS_EE=false jest --config ./jest.config.front.js",
38
+ "test:front:watch:ce": "run -T cross-env IS_EE=false jest --config ./jest.config.front.js --watchAll",
39
+ "test:front:ce:cov": "run -T cross-env IS_EE=false jest --config ./jest.config.front.js --coverage",
40
+ "lint": "run -T eslint ."
39
41
  },
40
42
  "dependencies": {
41
43
  "@babel/core": "^7.20.12",
@@ -46,19 +48,20 @@
46
48
  "@casl/ability": "^5.4.3",
47
49
  "@fingerprintjs/fingerprintjs": "3.3.6",
48
50
  "@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
49
- "@strapi/babel-plugin-switch-ee-ce": "4.10.0-beta.0",
50
- "@strapi/data-transfer": "4.10.0-beta.0",
51
+ "@strapi/babel-plugin-switch-ee-ce": "4.10.0",
52
+ "@strapi/data-transfer": "4.10.0",
51
53
  "@strapi/design-system": "1.6.6",
52
- "@strapi/helper-plugin": "4.10.0-beta.0",
54
+ "@strapi/helper-plugin": "4.10.0",
53
55
  "@strapi/icons": "1.6.6",
54
- "@strapi/permissions": "4.10.0-beta.0",
55
- "@strapi/provider-audit-logs-local": "4.10.0-beta.0",
56
- "@strapi/typescript-utils": "4.10.0-beta.0",
57
- "@strapi/utils": "4.10.0-beta.0",
56
+ "@strapi/permissions": "4.10.0",
57
+ "@strapi/provider-audit-logs-local": "4.10.0",
58
+ "@strapi/typescript-utils": "4.10.0",
59
+ "@strapi/utils": "4.10.0",
58
60
  "axios": "1.3.4",
59
61
  "babel-loader": "^9.1.2",
60
62
  "babel-plugin-styled-components": "2.0.2",
61
63
  "bcryptjs": "2.4.3",
64
+ "browserslist": "^4.17.3",
62
65
  "browserslist-to-esbuild": "1.2.0",
63
66
  "chalk": "^4.1.2",
64
67
  "chokidar": "^3.5.1",
@@ -99,18 +102,17 @@
99
102
  "markdown-it-mark": "^3.0.1",
100
103
  "markdown-it-sub": "^1.0.0",
101
104
  "markdown-it-sup": "1.0.0",
102
- "match-sorter": "^4.0.2",
103
105
  "mini-css-extract-plugin": "2.7.2",
104
106
  "node-schedule": "2.1.0",
105
107
  "p-map": "4.0.0",
106
108
  "passport-local": "1.0.0",
107
109
  "pluralize": "8.0.0",
108
110
  "prop-types": "^15.7.2",
109
- "qs": "6.11.0",
111
+ "qs": "6.11.1",
110
112
  "react": "^17.0.2",
111
113
  "react-copy-to-clipboard": "^5.1.0",
112
114
  "react-dnd": "15.1.2",
113
- "react-dnd-html5-backend": "15.1.2",
115
+ "react-dnd-html5-backend": "15.1.3",
114
116
  "react-dom": "^17.0.2",
115
117
  "react-error-boundary": "3.1.4",
116
118
  "react-fast-compare": "^3.2.0",
@@ -122,16 +124,16 @@
122
124
  "react-refresh": "0.14.0",
123
125
  "react-router-dom": "5.3.4",
124
126
  "react-select": "5.7.0",
125
- "react-window": "1.8.7",
127
+ "react-window": "1.8.8",
126
128
  "redux": "^4.2.1",
127
129
  "reselect": "^4.1.7",
128
130
  "rimraf": "3.0.2",
129
- "sanitize-html": "2.7.3",
131
+ "sanitize-html": "2.10.0",
130
132
  "semver": "7.3.8",
131
- "sift": "16.0.0",
133
+ "sift": "16.0.1",
132
134
  "style-loader": "3.3.1",
133
135
  "styled-components": "5.3.3",
134
- "typescript": "4.6.2",
136
+ "typescript": "5.0.4",
135
137
  "webpack": "^5.76.0",
136
138
  "webpack-cli": "^5.0.1",
137
139
  "webpack-dev-server": "^4.13.1",
@@ -166,5 +168,5 @@
166
168
  }
167
169
  }
168
170
  },
169
- "gitHead": "1519ef0e56d27b738f24fc88223797651ad47aaf"
171
+ "gitHead": "9b5519778faaedfb837879f9c6f7e28fdfd6750d"
170
172
  }
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const { createTransferHandler } = require('@strapi/data-transfer').strapi.remote.handlers;
3
+ const { createPushController, createPullController } =
4
+ require('@strapi/data-transfer').strapi.remote.handlers;
4
5
  const {
5
6
  errors: { UnauthorizedError },
6
7
  } = require('@strapi/utils');
@@ -22,5 +23,6 @@ const verify = async (ctx, scope) => {
22
23
  };
23
24
 
24
25
  module.exports = {
25
- connect: createTransferHandler({ verify }),
26
+ push: createPushController({ verify }),
27
+ pull: createPullController({ verify }),
26
28
  };
@@ -13,7 +13,10 @@ module.exports = () => async (ctx, next) => {
13
13
 
14
14
  if (!hasValidTokenSalt()) {
15
15
  return ctx.notImplemented(
16
- 'The server configuration for data transfer is invalid. Please contact your server administrator.'
16
+ 'The server configuration for data transfer is invalid. Please contact your server administrator.',
17
+ {
18
+ code: 'INVALID_TOKEN_SALT',
19
+ }
17
20
  );
18
21
  }
19
22
 
@@ -3,17 +3,26 @@
3
3
  const dataTransferAuthStrategy = require('../strategies/data-transfer');
4
4
 
5
5
  module.exports = [
6
- // Transfer route
6
+ // Transfer Push
7
7
  {
8
8
  method: 'GET',
9
- path: '/transfer/runner/connect',
10
- handler: 'transfer.runner-connect',
9
+ path: '/transfer/runner/push',
10
+ handler: 'transfer.runner-push',
11
11
  config: {
12
12
  middlewares: ['admin::data-transfer'],
13
- // TODO: Allow not passing any scope <> Add a way to prevent assigning one by default
14
13
  auth: { strategies: [dataTransferAuthStrategy], scope: ['push'] },
15
14
  },
16
15
  },
16
+ // Transfer Pull
17
+ {
18
+ method: 'GET',
19
+ path: '/transfer/runner/pull',
20
+ handler: 'transfer.runner-pull',
21
+ config: {
22
+ middlewares: ['admin::data-transfer'],
23
+ auth: { strategies: [dataTransferAuthStrategy], scope: ['pull'] },
24
+ },
25
+ },
17
26
  // Transfer Tokens
18
27
  {
19
28
  method: 'POST',
@@ -24,6 +24,10 @@ module.exports = {
24
24
  DAYS_30: 30 * DAY_IN_MS,
25
25
  DAYS_90: 90 * DAY_IN_MS,
26
26
  },
27
+ TRANSFER_TOKEN_TYPE: {
28
+ PUSH: 'push',
29
+ PULL: 'pull',
30
+ },
27
31
  TRANSFER_TOKEN_LIFESPANS: {
28
32
  UNLIMITED: null,
29
33
  DAYS_7: 7 * DAY_IN_MS,
@@ -3,7 +3,7 @@
3
3
  const permissions = require('@strapi/permissions');
4
4
  const { providerFactory } = require('@strapi/utils');
5
5
 
6
- const DEFAULT_TRANSFER_ACTIONS = ['push'];
6
+ const DEFAULT_TRANSFER_ACTIONS = ['push', 'pull'];
7
7
 
8
8
  const providers = {
9
9
  action: providerFactory(),