@strapi/content-manager 0.0.0-experimental.a65a85fdea97faae8679d3ffc5f9d79af61abd26 → 0.0.0-experimental.a6728ad43ac70ae19dabb624dbfca1f2d9610a86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -3
- package/dist/_chunks/{CardDragPreview-DSVYodBX.js → CardDragPreview-C0QyJgRA.js} +10 -14
- package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -0
- package/dist/_chunks/{CardDragPreview-ikSG4M46.mjs → CardDragPreview-DOxamsuj.mjs} +7 -9
- package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -0
- package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs → ComponentConfigurationPage-DJ5voqEK.mjs} +3 -3
- package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs.map → ComponentConfigurationPage-DJ5voqEK.mjs.map} +1 -1
- package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js → ComponentConfigurationPage-_6osrv39.js} +3 -3
- package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js.map → ComponentConfigurationPage-_6osrv39.js.map} +1 -1
- package/dist/_chunks/{ComponentIcon-BBQsYCVn.js → ComponentIcon-BXdiCGQp.js} +8 -2
- package/dist/_chunks/ComponentIcon-BXdiCGQp.js.map +1 -0
- package/dist/_chunks/{ComponentIcon-BOFnK76n.mjs → ComponentIcon-u4bIXTFY.mjs} +9 -3
- package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -0
- package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs → EditConfigurationPage-CZofxSLy.mjs} +3 -3
- package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs.map → EditConfigurationPage-CZofxSLy.mjs.map} +1 -1
- package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js → EditConfigurationPage-ZN3s568V.js} +3 -3
- package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js.map → EditConfigurationPage-ZN3s568V.js.map} +1 -1
- package/dist/_chunks/{EditViewPage-CzOT5Kpj.js → EditViewPage-Co2IKQZH.js} +58 -49
- package/dist/_chunks/EditViewPage-Co2IKQZH.js.map +1 -0
- package/dist/_chunks/{EditViewPage-Bm8lgcm6.mjs → EditViewPage-HYljoEY7.mjs} +59 -48
- package/dist/_chunks/EditViewPage-HYljoEY7.mjs.map +1 -0
- package/dist/_chunks/{Field-Dlh0uGnL.mjs → Field-BOPUMZ1u.mjs} +995 -795
- package/dist/_chunks/Field-BOPUMZ1u.mjs.map +1 -0
- package/dist/_chunks/{Field-Caef4JjM.js → Field-G9CkFUtP.js} +1041 -842
- package/dist/_chunks/Field-G9CkFUtP.js.map +1 -0
- package/dist/_chunks/{Form-EnaQL_6L.mjs → Form-CDwNp7pU.mjs} +69 -48
- package/dist/_chunks/Form-CDwNp7pU.mjs.map +1 -0
- package/dist/_chunks/{Form-BzuAjtRq.js → Form-crsbkGxI.js} +68 -48
- package/dist/_chunks/Form-crsbkGxI.js.map +1 -0
- package/dist/_chunks/{History-D6sbCJvo.mjs → History-BDZrgfZ3.mjs} +151 -57
- package/dist/_chunks/History-BDZrgfZ3.mjs.map +1 -0
- package/dist/_chunks/{History-C17LiyRg.js → History-CWcM9HnW.js} +151 -58
- package/dist/_chunks/History-CWcM9HnW.js.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-Ce4qs7qE.mjs → ListConfigurationPage-BZ3ScUna.mjs} +67 -57
- package/dist/_chunks/ListConfigurationPage-BZ3ScUna.mjs.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-Dks5SX6f.js → ListConfigurationPage-DGzoQD_I.js} +70 -61
- package/dist/_chunks/ListConfigurationPage-DGzoQD_I.js.map +1 -0
- package/dist/_chunks/{ListViewPage-BwrZrPsh.js → ListViewPage-BBAC9aPu.js} +132 -139
- package/dist/_chunks/ListViewPage-BBAC9aPu.js.map +1 -0
- package/dist/_chunks/{ListViewPage-Be7S5aKL.mjs → ListViewPage-CsX7tWx-.mjs} +129 -136
- package/dist/_chunks/ListViewPage-CsX7tWx-.mjs.map +1 -0
- package/dist/_chunks/{NoContentTypePage-Cu5r1-JT.js → NoContentTypePage-CwVDx_YC.js} +5 -5
- package/dist/_chunks/NoContentTypePage-CwVDx_YC.js.map +1 -0
- package/dist/_chunks/{NoContentTypePage-CIPmYQMm.mjs → NoContentTypePage-LClTUPWs.mjs} +7 -7
- package/dist/_chunks/NoContentTypePage-LClTUPWs.mjs.map +1 -0
- package/dist/_chunks/{NoPermissionsPage-C-j6TEUF.js → NoPermissionsPage-D2iWw-sn.js} +4 -5
- package/dist/_chunks/NoPermissionsPage-D2iWw-sn.js.map +1 -0
- package/dist/_chunks/{NoPermissionsPage-DhJ7LYrr.mjs → NoPermissionsPage-S4Re3FwO.mjs} +5 -6
- package/dist/_chunks/NoPermissionsPage-S4Re3FwO.mjs.map +1 -0
- package/dist/_chunks/{Relations-CY7AtkDA.mjs → Relations-Dmv0Tpe5.mjs} +67 -57
- package/dist/_chunks/Relations-Dmv0Tpe5.mjs.map +1 -0
- package/dist/_chunks/{Relations-Czs-uZ-s.js → Relations-jwuTFGOV.js} +71 -62
- package/dist/_chunks/Relations-jwuTFGOV.js.map +1 -0
- package/dist/_chunks/{en-C-V1_90f.js → en-BlhnxQfj.js} +17 -9
- package/dist/_chunks/{en-C-V1_90f.js.map → en-BlhnxQfj.js.map} +1 -1
- package/dist/_chunks/{en-MBPul9Su.mjs → en-C8YBvRrK.mjs} +17 -9
- package/dist/_chunks/{en-MBPul9Su.mjs.map → en-C8YBvRrK.mjs.map} +1 -1
- package/dist/_chunks/{index-DNVx8ssZ.mjs → index-BmUAydCA.mjs} +1715 -813
- package/dist/_chunks/index-BmUAydCA.mjs.map +1 -0
- package/dist/_chunks/{index-X_2tafck.js → index-CBX6KyXv.js} +1815 -914
- package/dist/_chunks/index-CBX6KyXv.js.map +1 -0
- package/dist/_chunks/{layout-Dnh0PNp9.mjs → layout-ClP-DC72.mjs} +47 -29
- package/dist/_chunks/layout-ClP-DC72.mjs.map +1 -0
- package/dist/_chunks/{layout-dBc7wN7L.js → layout-CxxkX9jY.js} +47 -31
- package/dist/_chunks/layout-CxxkX9jY.js.map +1 -0
- package/dist/_chunks/{relations-4pHtBrHJ.js → relations-DIjTADIu.js} +2 -2
- package/dist/_chunks/{relations-4pHtBrHJ.js.map → relations-DIjTADIu.js.map} +1 -1
- package/dist/_chunks/{relations-Dx7tMKJN.mjs → relations-op89RClB.mjs} +2 -2
- package/dist/_chunks/{relations-Dx7tMKJN.mjs.map → relations-op89RClB.mjs.map} +1 -1
- package/dist/_chunks/useDebounce-CtcjDB3L.js +28 -0
- package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
- package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
- package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
- package/dist/_chunks/useDragAndDrop-DdHgKsqq.mjs.map +1 -1
- package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +1 -1
- package/dist/admin/index.js +3 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +9 -7
- package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
- package/dist/admin/src/content-manager.d.ts +3 -3
- package/dist/admin/src/exports.d.ts +2 -1
- package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
- package/dist/admin/src/history/index.d.ts +3 -0
- package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
- package/dist/admin/src/hooks/useDocument.d.ts +35 -9
- package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
- package/dist/admin/src/hooks/useDocumentLayout.d.ts +2 -2
- package/dist/admin/src/hooks/useDragAndDrop.d.ts +4 -4
- package/dist/admin/src/hooks/useKeyboardDragAndDrop.d.ts +1 -1
- package/dist/admin/src/index.d.ts +1 -0
- package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +11 -4
- package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
- package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +30 -18
- package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +16 -53
- package/dist/admin/src/pages/EditView/components/Header.d.ts +10 -11
- package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
- package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
- package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
- package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
- package/dist/admin/src/services/api.d.ts +2 -3
- package/dist/admin/src/services/components.d.ts +2 -2
- package/dist/admin/src/services/contentTypes.d.ts +5 -5
- package/dist/admin/src/services/documents.d.ts +31 -17
- package/dist/admin/src/services/init.d.ts +2 -2
- package/dist/admin/src/services/relations.d.ts +3 -3
- package/dist/admin/src/services/uid.d.ts +3 -3
- package/dist/admin/src/utils/api.d.ts +4 -18
- package/dist/admin/src/utils/validation.d.ts +5 -7
- package/dist/server/index.js +648 -447
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +656 -455
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
- package/dist/server/src/controllers/relations.d.ts.map +1 -1
- package/dist/server/src/controllers/single-types.d.ts.map +1 -1
- package/dist/server/src/controllers/uid.d.ts.map +1 -1
- package/dist/server/src/controllers/utils/metadata.d.ts +8 -0
- package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/dimensions.d.ts +11 -0
- package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/index.d.ts +1 -1
- package/dist/server/src/history/services/history.d.ts +2 -4
- package/dist/server/src/history/services/history.d.ts.map +1 -1
- package/dist/server/src/history/services/index.d.ts +6 -2
- package/dist/server/src/history/services/index.d.ts.map +1 -1
- package/dist/server/src/history/services/lifecycles.d.ts +9 -0
- package/dist/server/src/history/services/lifecycles.d.ts.map +1 -0
- package/dist/server/src/history/services/utils.d.ts +42 -9
- package/dist/server/src/history/services/utils.d.ts.map +1 -1
- package/dist/server/src/history/utils.d.ts +6 -2
- package/dist/server/src/history/utils.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +18 -39
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
- package/dist/server/src/services/document-manager.d.ts +13 -12
- package/dist/server/src/services/document-manager.d.ts.map +1 -1
- package/dist/server/src/services/document-metadata.d.ts +8 -29
- package/dist/server/src/services/document-metadata.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +18 -39
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/permission-checker.d.ts.map +1 -1
- package/dist/server/src/services/utils/populate.d.ts +8 -1
- package/dist/server/src/services/utils/populate.d.ts.map +1 -1
- package/dist/shared/contracts/collection-types.d.ts +17 -7
- package/dist/shared/contracts/collection-types.d.ts.map +1 -1
- package/dist/shared/contracts/relations.d.ts +2 -2
- package/dist/shared/contracts/relations.d.ts.map +1 -1
- package/package.json +16 -17
- package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
- package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
- package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
- package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
- package/dist/_chunks/EditViewPage-Bm8lgcm6.mjs.map +0 -1
- package/dist/_chunks/EditViewPage-CzOT5Kpj.js.map +0 -1
- package/dist/_chunks/Field-Caef4JjM.js.map +0 -1
- package/dist/_chunks/Field-Dlh0uGnL.mjs.map +0 -1
- package/dist/_chunks/Form-BzuAjtRq.js.map +0 -1
- package/dist/_chunks/Form-EnaQL_6L.mjs.map +0 -1
- package/dist/_chunks/History-C17LiyRg.js.map +0 -1
- package/dist/_chunks/History-D6sbCJvo.mjs.map +0 -1
- package/dist/_chunks/ListConfigurationPage-Ce4qs7qE.mjs.map +0 -1
- package/dist/_chunks/ListConfigurationPage-Dks5SX6f.js.map +0 -1
- package/dist/_chunks/ListViewPage-Be7S5aKL.mjs.map +0 -1
- package/dist/_chunks/ListViewPage-BwrZrPsh.js.map +0 -1
- package/dist/_chunks/NoContentTypePage-CIPmYQMm.mjs.map +0 -1
- package/dist/_chunks/NoContentTypePage-Cu5r1-JT.js.map +0 -1
- package/dist/_chunks/NoPermissionsPage-C-j6TEUF.js.map +0 -1
- package/dist/_chunks/NoPermissionsPage-DhJ7LYrr.mjs.map +0 -1
- package/dist/_chunks/Relations-CY7AtkDA.mjs.map +0 -1
- package/dist/_chunks/Relations-Czs-uZ-s.js.map +0 -1
- package/dist/_chunks/index-DNVx8ssZ.mjs.map +0 -1
- package/dist/_chunks/index-X_2tafck.js.map +0 -1
- package/dist/_chunks/layout-Dnh0PNp9.mjs.map +0 -1
- package/dist/_chunks/layout-dBc7wN7L.js.map +0 -1
- package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
- package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
- package/dist/_chunks/urls-DzZya_gm.js +0 -6
- package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
- package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
- package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
- package/strapi-server.js +0 -3
package/dist/server/index.js
CHANGED
@@ -138,43 +138,70 @@ const FIELDS_TO_IGNORE = [
|
|
138
138
|
"strapi_stage",
|
139
139
|
"strapi_assignee"
|
140
140
|
];
|
141
|
-
const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
|
142
|
-
const sanitizedContentTypeSchemaAttributes = fp.omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
|
143
|
-
const reduceDifferenceToAttributesObject = (diffKeys, source) => {
|
144
|
-
return diffKeys.reduce((previousAttributesObject, diffKey) => {
|
145
|
-
previousAttributesObject[diffKey] = source[diffKey];
|
146
|
-
return previousAttributesObject;
|
147
|
-
}, {});
|
148
|
-
};
|
149
|
-
const versionSchemaKeys = Object.keys(versionSchemaAttributes);
|
150
|
-
const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
|
151
|
-
const uniqueToContentType = fp.difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
|
152
|
-
const added = reduceDifferenceToAttributesObject(
|
153
|
-
uniqueToContentType,
|
154
|
-
sanitizedContentTypeSchemaAttributes
|
155
|
-
);
|
156
|
-
const uniqueToVersion = fp.difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
|
157
|
-
const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
|
158
|
-
return { added, removed };
|
159
|
-
};
|
160
141
|
const DEFAULT_RETENTION_DAYS = 90;
|
161
|
-
const
|
162
|
-
const
|
163
|
-
|
164
|
-
|
142
|
+
const createServiceUtils = ({ strapi: strapi2 }) => {
|
143
|
+
const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
|
144
|
+
const sanitizedContentTypeSchemaAttributes = fp.omit(
|
145
|
+
FIELDS_TO_IGNORE,
|
146
|
+
contentTypeSchemaAttributes
|
147
|
+
);
|
148
|
+
const reduceDifferenceToAttributesObject = (diffKeys, source) => {
|
149
|
+
return diffKeys.reduce(
|
150
|
+
(previousAttributesObject, diffKey) => {
|
151
|
+
previousAttributesObject[diffKey] = source[diffKey];
|
152
|
+
return previousAttributesObject;
|
153
|
+
},
|
154
|
+
{}
|
155
|
+
);
|
156
|
+
};
|
157
|
+
const versionSchemaKeys = Object.keys(versionSchemaAttributes);
|
158
|
+
const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
|
159
|
+
const uniqueToContentType = fp.difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
|
160
|
+
const added = reduceDifferenceToAttributesObject(
|
161
|
+
uniqueToContentType,
|
162
|
+
sanitizedContentTypeSchemaAttributes
|
163
|
+
);
|
164
|
+
const uniqueToVersion = fp.difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
|
165
|
+
const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
|
166
|
+
return { added, removed };
|
165
167
|
};
|
166
|
-
const
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
168
|
+
const getRelationRestoreValue = async (versionRelationData, attribute) => {
|
169
|
+
if (Array.isArray(versionRelationData)) {
|
170
|
+
if (versionRelationData.length === 0)
|
171
|
+
return versionRelationData;
|
172
|
+
const existingAndMissingRelations = await Promise.all(
|
173
|
+
versionRelationData.map((relation) => {
|
174
|
+
return strapi2.documents(attribute.target).findOne({
|
175
|
+
documentId: relation.documentId,
|
176
|
+
locale: relation.locale || void 0
|
177
|
+
});
|
178
|
+
})
|
179
|
+
);
|
180
|
+
return existingAndMissingRelations.filter(
|
181
|
+
(relation) => relation !== null
|
182
|
+
);
|
173
183
|
}
|
174
|
-
return
|
184
|
+
return strapi2.documents(attribute.target).findOne({
|
185
|
+
documentId: versionRelationData.documentId,
|
186
|
+
locale: versionRelationData.locale || void 0
|
187
|
+
});
|
188
|
+
};
|
189
|
+
const getMediaRestoreValue = async (versionRelationData, attribute) => {
|
190
|
+
if (attribute.multiple) {
|
191
|
+
const existingAndMissingMedias = await Promise.all(
|
192
|
+
// @ts-expect-error Fix the type definitions so this isn't any
|
193
|
+
versionRelationData.map((media) => {
|
194
|
+
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
|
195
|
+
})
|
196
|
+
);
|
197
|
+
return existingAndMissingMedias.filter((media) => media != null);
|
198
|
+
}
|
199
|
+
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
|
175
200
|
};
|
176
201
|
const localesService = strapi2.plugin("i18n")?.service("locales");
|
202
|
+
const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
|
177
203
|
const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
|
204
|
+
const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
|
178
205
|
const getLocaleDictionary = async () => {
|
179
206
|
if (!localesService)
|
180
207
|
return {};
|
@@ -187,25 +214,39 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
187
214
|
{}
|
188
215
|
);
|
189
216
|
};
|
217
|
+
const getRetentionDays = () => {
|
218
|
+
const featureConfig = strapi2.ee.features.get("cms-content-history");
|
219
|
+
const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
|
220
|
+
const userRetentionDays = strapi2.config.get("admin.history.retentionDays");
|
221
|
+
if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
|
222
|
+
return userRetentionDays;
|
223
|
+
}
|
224
|
+
return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
|
225
|
+
};
|
190
226
|
const getVersionStatus = async (contentTypeUid, document) => {
|
191
227
|
const documentMetadataService = strapi2.plugin("content-manager").service("document-metadata");
|
192
228
|
const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
|
193
229
|
return documentMetadataService.getStatus(document, meta.availableStatus);
|
194
230
|
};
|
195
|
-
const getDeepPopulate2 = (uid2) => {
|
231
|
+
const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
|
196
232
|
const model = strapi2.getModel(uid2);
|
197
233
|
const attributes = Object.entries(model.attributes);
|
234
|
+
const fieldSelector = useDatabaseSyntax ? "select" : "fields";
|
198
235
|
return attributes.reduce((acc, [attributeName, attribute]) => {
|
199
236
|
switch (attribute.type) {
|
200
237
|
case "relation": {
|
238
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
239
|
+
if (isMorphRelation) {
|
240
|
+
break;
|
241
|
+
}
|
201
242
|
const isVisible2 = strapiUtils.contentTypes.isVisibleAttribute(model, attributeName);
|
202
243
|
if (isVisible2) {
|
203
|
-
acc[attributeName] = {
|
244
|
+
acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
|
204
245
|
}
|
205
246
|
break;
|
206
247
|
}
|
207
248
|
case "media": {
|
208
|
-
acc[attributeName] = {
|
249
|
+
acc[attributeName] = { [fieldSelector]: ["id"] };
|
209
250
|
break;
|
210
251
|
}
|
211
252
|
case "component": {
|
@@ -228,80 +269,69 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
228
269
|
return acc;
|
229
270
|
}, {});
|
230
271
|
};
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
238
|
-
return next();
|
272
|
+
const buildMediaResponse = async (values) => {
|
273
|
+
return values.slice(0, 25).reduce(
|
274
|
+
async (currentRelationDataPromise, entry) => {
|
275
|
+
const currentRelationData = await currentRelationDataPromise;
|
276
|
+
if (!entry) {
|
277
|
+
return currentRelationData;
|
239
278
|
}
|
240
|
-
|
241
|
-
|
279
|
+
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
280
|
+
if (relatedEntry) {
|
281
|
+
currentRelationData.results.push(relatedEntry);
|
282
|
+
} else {
|
283
|
+
currentRelationData.meta.missingCount += 1;
|
242
284
|
}
|
243
|
-
|
244
|
-
|
245
|
-
|
285
|
+
return currentRelationData;
|
286
|
+
},
|
287
|
+
Promise.resolve({
|
288
|
+
results: [],
|
289
|
+
meta: { missingCount: 0 }
|
290
|
+
})
|
291
|
+
);
|
292
|
+
};
|
293
|
+
const buildRelationReponse = async (values, attributeSchema) => {
|
294
|
+
return values.slice(0, 25).reduce(
|
295
|
+
async (currentRelationDataPromise, entry) => {
|
296
|
+
const currentRelationData = await currentRelationDataPromise;
|
297
|
+
if (!entry) {
|
298
|
+
return currentRelationData;
|
246
299
|
}
|
247
|
-
const
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
documentId: documentContext.documentId,
|
253
|
-
locale,
|
254
|
-
populate: getDeepPopulate2(contentTypeUid)
|
255
|
-
});
|
256
|
-
const status = await getVersionStatus(contentTypeUid, document);
|
257
|
-
const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
|
258
|
-
const componentsSchemas = Object.keys(
|
259
|
-
attributesSchema
|
260
|
-
).reduce((currentComponentSchemas, key) => {
|
261
|
-
const fieldSchema = attributesSchema[key];
|
262
|
-
if (fieldSchema.type === "component") {
|
263
|
-
const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
|
264
|
-
return {
|
265
|
-
...currentComponentSchemas,
|
266
|
-
[fieldSchema.component]: componentSchema
|
267
|
-
};
|
268
|
-
}
|
269
|
-
return currentComponentSchemas;
|
270
|
-
}, {});
|
271
|
-
await strapi2.db.transaction(async ({ onCommit }) => {
|
272
|
-
onCommit(() => {
|
273
|
-
this.createVersion({
|
274
|
-
contentType: contentTypeUid,
|
275
|
-
data: fp.omit(FIELDS_TO_IGNORE, document),
|
276
|
-
schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
|
277
|
-
componentsSchemas,
|
278
|
-
relatedDocumentId: documentContext.documentId,
|
279
|
-
locale,
|
280
|
-
status
|
281
|
-
});
|
300
|
+
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
301
|
+
if (relatedEntry) {
|
302
|
+
currentRelationData.results.push({
|
303
|
+
...relatedEntry,
|
304
|
+
status: await getVersionStatus(attributeSchema.target, relatedEntry)
|
282
305
|
});
|
283
|
-
}
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
306
|
+
} else {
|
307
|
+
currentRelationData.meta.missingCount += 1;
|
308
|
+
}
|
309
|
+
return currentRelationData;
|
310
|
+
},
|
311
|
+
Promise.resolve({
|
312
|
+
results: [],
|
313
|
+
meta: { missingCount: 0 }
|
314
|
+
})
|
315
|
+
);
|
316
|
+
};
|
317
|
+
return {
|
318
|
+
getSchemaAttributesDiff,
|
319
|
+
getRelationRestoreValue,
|
320
|
+
getMediaRestoreValue,
|
321
|
+
getDefaultLocale,
|
322
|
+
isLocalizedContentType,
|
323
|
+
getLocaleDictionary,
|
324
|
+
getRetentionDays,
|
325
|
+
getVersionStatus,
|
326
|
+
getDeepPopulate: getDeepPopulate2,
|
327
|
+
buildMediaResponse,
|
328
|
+
buildRelationReponse
|
329
|
+
};
|
330
|
+
};
|
331
|
+
const createHistoryService = ({ strapi: strapi2 }) => {
|
332
|
+
const query = strapi2.db.query(HISTORY_VERSION_UID);
|
333
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
334
|
+
return {
|
305
335
|
async createVersion(historyVersionData) {
|
306
336
|
await query.create({
|
307
337
|
data: {
|
@@ -312,7 +342,13 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
312
342
|
});
|
313
343
|
},
|
314
344
|
async findVersionsPage(params) {
|
315
|
-
const
|
345
|
+
const model = strapi2.getModel(params.query.contentType);
|
346
|
+
const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
|
347
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
348
|
+
let locale = null;
|
349
|
+
if (isLocalizedContentType) {
|
350
|
+
locale = params.query.locale || defaultLocale;
|
351
|
+
}
|
316
352
|
const [{ results, pagination }, localeDictionary] = await Promise.all([
|
317
353
|
query.findPage({
|
318
354
|
...params.query,
|
@@ -326,78 +362,34 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
326
362
|
populate: ["createdBy"],
|
327
363
|
orderBy: [{ createdAt: "desc" }]
|
328
364
|
}),
|
329
|
-
getLocaleDictionary()
|
365
|
+
serviceUtils.getLocaleDictionary()
|
330
366
|
]);
|
331
|
-
const buildRelationReponse = async (values, attributeSchema) => {
|
332
|
-
return values.slice(0, 25).reduce(
|
333
|
-
async (currentRelationDataPromise, entry) => {
|
334
|
-
const currentRelationData = await currentRelationDataPromise;
|
335
|
-
if (!entry) {
|
336
|
-
return currentRelationData;
|
337
|
-
}
|
338
|
-
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
339
|
-
const permissionChecker2 = getService$1("permission-checker").create({
|
340
|
-
userAbility: params.state.userAbility,
|
341
|
-
model: attributeSchema.target
|
342
|
-
});
|
343
|
-
const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
|
344
|
-
if (sanitizedEntry) {
|
345
|
-
currentRelationData.results.push({
|
346
|
-
...sanitizedEntry,
|
347
|
-
status: await getVersionStatus(attributeSchema.target, sanitizedEntry)
|
348
|
-
});
|
349
|
-
} else {
|
350
|
-
currentRelationData.meta.missingCount += 1;
|
351
|
-
}
|
352
|
-
return currentRelationData;
|
353
|
-
},
|
354
|
-
Promise.resolve({
|
355
|
-
results: [],
|
356
|
-
meta: { missingCount: 0 }
|
357
|
-
})
|
358
|
-
);
|
359
|
-
};
|
360
|
-
const buildMediaResponse = async (values) => {
|
361
|
-
return values.slice(0, 25).reduce(
|
362
|
-
async (currentRelationDataPromise, entry) => {
|
363
|
-
const currentRelationData = await currentRelationDataPromise;
|
364
|
-
if (!entry) {
|
365
|
-
return currentRelationData;
|
366
|
-
}
|
367
|
-
const permissionChecker2 = getService$1("permission-checker").create({
|
368
|
-
userAbility: params.state.userAbility,
|
369
|
-
model: "plugin::upload.file"
|
370
|
-
});
|
371
|
-
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
372
|
-
const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
|
373
|
-
if (sanitizedEntry) {
|
374
|
-
currentRelationData.results.push(sanitizedEntry);
|
375
|
-
} else {
|
376
|
-
currentRelationData.meta.missingCount += 1;
|
377
|
-
}
|
378
|
-
return currentRelationData;
|
379
|
-
},
|
380
|
-
Promise.resolve({
|
381
|
-
results: [],
|
382
|
-
meta: { missingCount: 0 }
|
383
|
-
})
|
384
|
-
);
|
385
|
-
};
|
386
367
|
const populateEntryRelations = async (entry) => {
|
387
368
|
const entryWithRelations = await Object.entries(entry.schema).reduce(
|
388
369
|
async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
|
389
370
|
const attributeValue = entry.data[attributeKey];
|
390
371
|
const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
|
391
372
|
if (attributeSchema.type === "media") {
|
373
|
+
const permissionChecker2 = getService$1("permission-checker").create({
|
374
|
+
userAbility: params.state.userAbility,
|
375
|
+
model: "plugin::upload.file"
|
376
|
+
});
|
377
|
+
const response = await serviceUtils.buildMediaResponse(attributeValues);
|
378
|
+
const sanitizedResults = await Promise.all(
|
379
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
380
|
+
);
|
392
381
|
return {
|
393
382
|
...await currentDataWithRelations,
|
394
|
-
[attributeKey]:
|
383
|
+
[attributeKey]: {
|
384
|
+
results: sanitizedResults,
|
385
|
+
meta: response.meta
|
386
|
+
}
|
395
387
|
};
|
396
388
|
}
|
397
389
|
if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
|
398
390
|
if (attributeSchema.target === "admin::user") {
|
399
391
|
const adminUsers = await Promise.all(
|
400
|
-
attributeValues.map(
|
392
|
+
attributeValues.map((userToPopulate) => {
|
401
393
|
if (userToPopulate == null) {
|
402
394
|
return null;
|
403
395
|
}
|
@@ -414,9 +406,23 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
414
406
|
[attributeKey]: adminUsers
|
415
407
|
};
|
416
408
|
}
|
409
|
+
const permissionChecker2 = getService$1("permission-checker").create({
|
410
|
+
userAbility: params.state.userAbility,
|
411
|
+
model: attributeSchema.target
|
412
|
+
});
|
413
|
+
const response = await serviceUtils.buildRelationReponse(
|
414
|
+
attributeValues,
|
415
|
+
attributeSchema
|
416
|
+
);
|
417
|
+
const sanitizedResults = await Promise.all(
|
418
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
419
|
+
);
|
417
420
|
return {
|
418
421
|
...await currentDataWithRelations,
|
419
|
-
[attributeKey]:
|
422
|
+
[attributeKey]: {
|
423
|
+
results: sanitizedResults,
|
424
|
+
meta: response.meta
|
425
|
+
}
|
420
426
|
};
|
421
427
|
}
|
422
428
|
return currentDataWithRelations;
|
@@ -431,7 +437,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
431
437
|
...result,
|
432
438
|
data: await populateEntryRelations(result),
|
433
439
|
meta: {
|
434
|
-
unknownAttributes: getSchemaAttributesDiff(
|
440
|
+
unknownAttributes: serviceUtils.getSchemaAttributesDiff(
|
435
441
|
result.schema,
|
436
442
|
strapi2.getModel(params.query.contentType).attributes
|
437
443
|
)
|
@@ -448,7 +454,10 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
448
454
|
async restoreVersion(versionId) {
|
449
455
|
const version = await query.findOne({ where: { id: versionId } });
|
450
456
|
const contentTypeSchemaAttributes = strapi2.getModel(version.contentType).attributes;
|
451
|
-
const schemaDiff = getSchemaAttributesDiff(
|
457
|
+
const schemaDiff = serviceUtils.getSchemaAttributesDiff(
|
458
|
+
version.schema,
|
459
|
+
contentTypeSchemaAttributes
|
460
|
+
);
|
452
461
|
const dataWithoutAddedAttributes = Object.keys(schemaDiff.added).reduce(
|
453
462
|
(currentData, addedKey) => {
|
454
463
|
currentData[addedKey] = null;
|
@@ -461,61 +470,26 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
461
470
|
FIELDS_TO_IGNORE,
|
462
471
|
contentTypeSchemaAttributes
|
463
472
|
);
|
464
|
-
const
|
465
|
-
|
466
|
-
|
467
|
-
const
|
468
|
-
if (
|
473
|
+
const reducer = strapiUtils.async.reduce(Object.entries(sanitizedSchemaAttributes));
|
474
|
+
const dataWithoutMissingRelations = await reducer(
|
475
|
+
async (previousRelationAttributes, [name, attribute]) => {
|
476
|
+
const versionRelationData = version.data[name];
|
477
|
+
if (!versionRelationData) {
|
469
478
|
return previousRelationAttributes;
|
470
479
|
}
|
471
480
|
if (attribute.type === "relation" && // TODO: handle polymorphic relations
|
472
481
|
attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
|
473
|
-
|
474
|
-
|
475
|
-
return previousRelationAttributes;
|
476
|
-
const existingAndMissingRelations = await Promise.all(
|
477
|
-
relationData.map((relation) => {
|
478
|
-
return strapi2.documents(attribute.target).findOne({
|
479
|
-
documentId: relation.documentId,
|
480
|
-
locale: relation.locale || void 0
|
481
|
-
});
|
482
|
-
})
|
483
|
-
);
|
484
|
-
const existingRelations = existingAndMissingRelations.filter(
|
485
|
-
(relation) => relation !== null
|
486
|
-
);
|
487
|
-
previousRelationAttributes[name] = existingRelations;
|
488
|
-
} else {
|
489
|
-
const existingRelation = await strapi2.documents(attribute.target).findOne({
|
490
|
-
documentId: relationData.documentId,
|
491
|
-
locale: relationData.locale || void 0
|
492
|
-
});
|
493
|
-
if (!existingRelation) {
|
494
|
-
previousRelationAttributes[name] = null;
|
495
|
-
}
|
496
|
-
}
|
482
|
+
const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
|
483
|
+
previousRelationAttributes[name] = data2;
|
497
484
|
}
|
498
485
|
if (attribute.type === "media") {
|
499
|
-
|
500
|
-
|
501
|
-
// @ts-expect-error Fix the type definitions so this isn't any
|
502
|
-
relationData.map((media) => {
|
503
|
-
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
|
504
|
-
})
|
505
|
-
);
|
506
|
-
const existingMedias = existingAndMissingMedias.filter((media) => media != null);
|
507
|
-
previousRelationAttributes[name] = existingMedias;
|
508
|
-
} else {
|
509
|
-
const existingMedia = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: version.data[name].id } });
|
510
|
-
if (!existingMedia) {
|
511
|
-
previousRelationAttributes[name] = null;
|
512
|
-
}
|
513
|
-
}
|
486
|
+
const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
|
487
|
+
previousRelationAttributes[name] = data2;
|
514
488
|
}
|
515
489
|
return previousRelationAttributes;
|
516
490
|
},
|
517
491
|
// Clone to avoid mutating the original version data
|
518
|
-
|
492
|
+
structuredClone(dataWithoutAddedAttributes)
|
519
493
|
);
|
520
494
|
const data = fp.omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
|
521
495
|
const restoredDocument = await strapi2.documents(version.contentType).update({
|
@@ -530,8 +504,120 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
530
504
|
}
|
531
505
|
};
|
532
506
|
};
|
507
|
+
const shouldCreateHistoryVersion = (context) => {
|
508
|
+
if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
509
|
+
return false;
|
510
|
+
}
|
511
|
+
if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
|
512
|
+
return false;
|
513
|
+
}
|
514
|
+
if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
|
515
|
+
return false;
|
516
|
+
}
|
517
|
+
if (!context.contentType.uid.startsWith("api::")) {
|
518
|
+
return false;
|
519
|
+
}
|
520
|
+
return true;
|
521
|
+
};
|
522
|
+
const getSchemas = (uid2) => {
|
523
|
+
const attributesSchema = strapi.getModel(uid2).attributes;
|
524
|
+
const componentsSchemas = Object.keys(attributesSchema).reduce(
|
525
|
+
(currentComponentSchemas, key) => {
|
526
|
+
const fieldSchema = attributesSchema[key];
|
527
|
+
if (fieldSchema.type === "component") {
|
528
|
+
const componentSchema = strapi.getModel(fieldSchema.component).attributes;
|
529
|
+
return {
|
530
|
+
...currentComponentSchemas,
|
531
|
+
[fieldSchema.component]: componentSchema
|
532
|
+
};
|
533
|
+
}
|
534
|
+
return currentComponentSchemas;
|
535
|
+
},
|
536
|
+
{}
|
537
|
+
);
|
538
|
+
return {
|
539
|
+
schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
|
540
|
+
componentsSchemas
|
541
|
+
};
|
542
|
+
};
|
543
|
+
const createLifecyclesService = ({ strapi: strapi2 }) => {
|
544
|
+
const state = {
|
545
|
+
deleteExpiredJob: null,
|
546
|
+
isInitialized: false
|
547
|
+
};
|
548
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
549
|
+
return {
|
550
|
+
async bootstrap() {
|
551
|
+
if (state.isInitialized) {
|
552
|
+
return;
|
553
|
+
}
|
554
|
+
strapi2.documents.use(async (context, next) => {
|
555
|
+
const result = await next();
|
556
|
+
if (!shouldCreateHistoryVersion(context)) {
|
557
|
+
return result;
|
558
|
+
}
|
559
|
+
const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
|
560
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
561
|
+
const locales = fp.castArray(context.params?.locale || defaultLocale);
|
562
|
+
if (!locales.length) {
|
563
|
+
return result;
|
564
|
+
}
|
565
|
+
const uid2 = context.contentType.uid;
|
566
|
+
const schemas = getSchemas(uid2);
|
567
|
+
const model = strapi2.getModel(uid2);
|
568
|
+
const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
|
569
|
+
const localeEntries = await strapi2.db.query(uid2).findMany({
|
570
|
+
where: {
|
571
|
+
documentId,
|
572
|
+
...isLocalizedContentType ? { locale: { $in: locales } } : {},
|
573
|
+
...strapiUtils.contentTypes.hasDraftAndPublish(strapi2.contentTypes[uid2]) ? { publishedAt: null } : {}
|
574
|
+
},
|
575
|
+
populate: serviceUtils.getDeepPopulate(
|
576
|
+
uid2,
|
577
|
+
true
|
578
|
+
/* use database syntax */
|
579
|
+
)
|
580
|
+
});
|
581
|
+
await strapi2.db.transaction(async ({ onCommit }) => {
|
582
|
+
onCommit(async () => {
|
583
|
+
for (const entry of localeEntries) {
|
584
|
+
const status = await serviceUtils.getVersionStatus(uid2, entry);
|
585
|
+
await getService(strapi2, "history").createVersion({
|
586
|
+
contentType: uid2,
|
587
|
+
data: fp.omit(FIELDS_TO_IGNORE, entry),
|
588
|
+
relatedDocumentId: documentId,
|
589
|
+
locale: entry.locale,
|
590
|
+
status,
|
591
|
+
...schemas
|
592
|
+
});
|
593
|
+
}
|
594
|
+
});
|
595
|
+
});
|
596
|
+
return result;
|
597
|
+
});
|
598
|
+
state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
|
599
|
+
const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
|
600
|
+
const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
|
601
|
+
strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
|
602
|
+
where: {
|
603
|
+
created_at: {
|
604
|
+
$lt: expirationDate.toISOString()
|
605
|
+
}
|
606
|
+
}
|
607
|
+
});
|
608
|
+
});
|
609
|
+
state.isInitialized = true;
|
610
|
+
},
|
611
|
+
async destroy() {
|
612
|
+
if (state.deleteExpiredJob) {
|
613
|
+
state.deleteExpiredJob.cancel();
|
614
|
+
}
|
615
|
+
}
|
616
|
+
};
|
617
|
+
};
|
533
618
|
const services$1 = {
|
534
|
-
history: createHistoryService
|
619
|
+
history: createHistoryService,
|
620
|
+
lifecycles: createLifecyclesService
|
535
621
|
};
|
536
622
|
const info = { pluginName: "content-manager", type: "admin" };
|
537
623
|
const historyVersionRouter = {
|
@@ -611,10 +697,10 @@ const getFeature = () => {
|
|
611
697
|
strapi2.get("models").add(historyVersion);
|
612
698
|
},
|
613
699
|
bootstrap({ strapi: strapi2 }) {
|
614
|
-
getService(strapi2, "
|
700
|
+
getService(strapi2, "lifecycles").bootstrap();
|
615
701
|
},
|
616
702
|
destroy({ strapi: strapi2 }) {
|
617
|
-
getService(strapi2, "
|
703
|
+
getService(strapi2, "lifecycles").destroy();
|
618
704
|
},
|
619
705
|
controllers: controllers$1,
|
620
706
|
services: services$1,
|
@@ -1144,6 +1230,11 @@ const { createPolicy } = strapiUtils.policy;
|
|
1144
1230
|
const hasPermissions = createPolicy({
|
1145
1231
|
name: "plugin::content-manager.hasPermissions",
|
1146
1232
|
validator: validateHasPermissionsInput,
|
1233
|
+
/**
|
1234
|
+
* NOTE: Action aliases are currently not checked at this level (policy).
|
1235
|
+
* This is currently the intended behavior to avoid changing the behavior of API related permissions.
|
1236
|
+
* If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
|
1237
|
+
*/
|
1147
1238
|
handler(ctx, config = {}) {
|
1148
1239
|
const { actions = [], hasAtLeastOne = false } = config;
|
1149
1240
|
const { userAbility } = ctx.state;
|
@@ -1433,7 +1524,7 @@ const { PaginationError, ValidationError } = strapiUtils.errors;
|
|
1433
1524
|
const TYPES = ["singleType", "collectionType"];
|
1434
1525
|
const kindSchema = strapiUtils.yup.string().oneOf(TYPES).nullable();
|
1435
1526
|
const bulkActionInputSchema = strapiUtils.yup.object({
|
1436
|
-
|
1527
|
+
documentIds: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
|
1437
1528
|
}).required();
|
1438
1529
|
const generateUIDInputSchema = strapiUtils.yup.object({
|
1439
1530
|
contentTypeUID: strapiUtils.yup.string().required(),
|
@@ -1532,15 +1623,49 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
|
|
1532
1623
|
}
|
1533
1624
|
}, body);
|
1534
1625
|
};
|
1535
|
-
const
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1626
|
+
const singleLocaleSchema = strapiUtils.yup.string().nullable();
|
1627
|
+
const multipleLocaleSchema = strapiUtils.yup.lazy(
|
1628
|
+
(value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
|
1629
|
+
);
|
1630
|
+
const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
|
1631
|
+
const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
|
1632
|
+
const { allowMultipleLocales } = opts;
|
1633
|
+
const { locale, status: providedStatus, ...rest } = request || {};
|
1634
|
+
const defaultStatus = strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
|
1635
|
+
const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
|
1636
|
+
const schema = strapiUtils.yup.object().shape({
|
1637
|
+
locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
|
1638
|
+
status: statusSchema
|
1639
|
+
});
|
1640
|
+
try {
|
1641
|
+
await strapiUtils.validateYupSchema(schema, { strict: true, abortEarly: false })(request);
|
1642
|
+
return { locale, status, ...rest };
|
1643
|
+
} catch (error) {
|
1644
|
+
throw new strapiUtils.errors.ValidationError(`Validation error: ${error.message}`);
|
1542
1645
|
}
|
1543
|
-
|
1646
|
+
};
|
1647
|
+
const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
|
1648
|
+
const documentMetadata2 = getService$1("document-metadata");
|
1649
|
+
const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
|
1650
|
+
let {
|
1651
|
+
meta: { availableLocales, availableStatus }
|
1652
|
+
} = serviceOutput;
|
1653
|
+
const metadataSanitizer = permissionChecker2.sanitizeOutput;
|
1654
|
+
availableLocales = await strapiUtils.async.map(
|
1655
|
+
availableLocales,
|
1656
|
+
async (localeDocument) => metadataSanitizer(localeDocument)
|
1657
|
+
);
|
1658
|
+
availableStatus = await strapiUtils.async.map(
|
1659
|
+
availableStatus,
|
1660
|
+
async (statusDocument) => metadataSanitizer(statusDocument)
|
1661
|
+
);
|
1662
|
+
return {
|
1663
|
+
...serviceOutput,
|
1664
|
+
meta: {
|
1665
|
+
availableLocales,
|
1666
|
+
availableStatus
|
1667
|
+
}
|
1668
|
+
};
|
1544
1669
|
};
|
1545
1670
|
const createDocument = async (ctx, opts) => {
|
1546
1671
|
const { userAbility, user } = ctx.state;
|
@@ -1555,7 +1680,7 @@ const createDocument = async (ctx, opts) => {
|
|
1555
1680
|
const setCreator = strapiUtils.setCreatorFields({ user });
|
1556
1681
|
const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
|
1557
1682
|
const sanitizedBody = await sanitizeFn(body);
|
1558
|
-
const { locale, status
|
1683
|
+
const { locale, status } = await getDocumentLocaleAndStatus(body, model);
|
1559
1684
|
return documentManager2.create(model, {
|
1560
1685
|
data: sanitizedBody,
|
1561
1686
|
locale,
|
@@ -1574,7 +1699,7 @@ const updateDocument = async (ctx, opts) => {
|
|
1574
1699
|
}
|
1575
1700
|
const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
|
1576
1701
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1577
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1702
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1578
1703
|
const [documentVersion, documentExists] = await Promise.all([
|
1579
1704
|
documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
|
1580
1705
|
documentManager2.exists(model, id)
|
@@ -1612,7 +1737,7 @@ const collectionTypes = {
|
|
1612
1737
|
}
|
1613
1738
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
1614
1739
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
|
1615
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
1740
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
1616
1741
|
const { results: documents, pagination } = await documentManager2.findPage(
|
1617
1742
|
{ ...permissionQuery, populate, locale, status },
|
1618
1743
|
model
|
@@ -1641,14 +1766,13 @@ const collectionTypes = {
|
|
1641
1766
|
const { userAbility } = ctx.state;
|
1642
1767
|
const { model, id } = ctx.params;
|
1643
1768
|
const documentManager2 = getService$1("document-manager");
|
1644
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1645
1769
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1646
1770
|
if (permissionChecker2.cannot.read()) {
|
1647
1771
|
return ctx.forbidden();
|
1648
1772
|
}
|
1649
1773
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1650
1774
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1651
|
-
const { locale, status
|
1775
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1652
1776
|
const version = await documentManager2.findOne(id, model, {
|
1653
1777
|
populate,
|
1654
1778
|
locale,
|
@@ -1659,9 +1783,11 @@ const collectionTypes = {
|
|
1659
1783
|
if (!exists) {
|
1660
1784
|
return ctx.notFound();
|
1661
1785
|
}
|
1662
|
-
const { meta } = await
|
1786
|
+
const { meta } = await formatDocumentWithMetadata(
|
1787
|
+
permissionChecker2,
|
1663
1788
|
model,
|
1664
|
-
|
1789
|
+
// @ts-expect-error TODO: fix
|
1790
|
+
{ documentId: id, locale, publishedAt: null },
|
1665
1791
|
{ availableLocales: true, availableStatus: false }
|
1666
1792
|
);
|
1667
1793
|
ctx.body = { data: {}, meta };
|
@@ -1671,12 +1797,11 @@ const collectionTypes = {
|
|
1671
1797
|
return ctx.forbidden();
|
1672
1798
|
}
|
1673
1799
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
1674
|
-
ctx.body = await
|
1800
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1675
1801
|
},
|
1676
1802
|
async create(ctx) {
|
1677
1803
|
const { userAbility } = ctx.state;
|
1678
1804
|
const { model } = ctx.params;
|
1679
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1680
1805
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1681
1806
|
const [totalEntries, document] = await Promise.all([
|
1682
1807
|
strapi.db.query(model).count(),
|
@@ -1684,7 +1809,7 @@ const collectionTypes = {
|
|
1684
1809
|
]);
|
1685
1810
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
1686
1811
|
ctx.status = 201;
|
1687
|
-
ctx.body = await
|
1812
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1688
1813
|
// Empty metadata as it's not relevant for a new document
|
1689
1814
|
availableLocales: false,
|
1690
1815
|
availableStatus: false
|
@@ -1698,25 +1823,23 @@ const collectionTypes = {
|
|
1698
1823
|
async update(ctx) {
|
1699
1824
|
const { userAbility } = ctx.state;
|
1700
1825
|
const { model } = ctx.params;
|
1701
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1702
1826
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1703
1827
|
const updatedVersion = await updateDocument(ctx);
|
1704
1828
|
const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
|
1705
|
-
ctx.body = await
|
1829
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
|
1706
1830
|
},
|
1707
1831
|
async clone(ctx) {
|
1708
1832
|
const { userAbility, user } = ctx.state;
|
1709
1833
|
const { model, sourceId: id } = ctx.params;
|
1710
1834
|
const { body } = ctx.request;
|
1711
1835
|
const documentManager2 = getService$1("document-manager");
|
1712
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1713
1836
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1714
1837
|
if (permissionChecker2.cannot.create()) {
|
1715
1838
|
return ctx.forbidden();
|
1716
1839
|
}
|
1717
1840
|
const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
|
1718
1841
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1719
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1842
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1720
1843
|
const document = await documentManager2.findOne(id, model, {
|
1721
1844
|
populate,
|
1722
1845
|
locale,
|
@@ -1732,7 +1855,7 @@ const collectionTypes = {
|
|
1732
1855
|
const sanitizedBody = await sanitizeFn(body);
|
1733
1856
|
const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
|
1734
1857
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
|
1735
|
-
ctx.body = await
|
1858
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1736
1859
|
// Empty metadata as it's not relevant for a new document
|
1737
1860
|
availableLocales: false,
|
1738
1861
|
availableStatus: false
|
@@ -1761,7 +1884,7 @@ const collectionTypes = {
|
|
1761
1884
|
}
|
1762
1885
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
|
1763
1886
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1764
|
-
const { locale } = getDocumentLocaleAndStatus(ctx.query);
|
1887
|
+
const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1765
1888
|
const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
|
1766
1889
|
if (documentLocales.length === 0) {
|
1767
1890
|
return ctx.notFound();
|
@@ -1783,7 +1906,6 @@ const collectionTypes = {
|
|
1783
1906
|
const { id, model } = ctx.params;
|
1784
1907
|
const { body } = ctx.request;
|
1785
1908
|
const documentManager2 = getService$1("document-manager");
|
1786
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1787
1909
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1788
1910
|
if (permissionChecker2.cannot.publish()) {
|
1789
1911
|
return ctx.forbidden();
|
@@ -1791,25 +1913,46 @@ const collectionTypes = {
|
|
1791
1913
|
const publishedDocument = await strapi.db.transaction(async () => {
|
1792
1914
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1793
1915
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1794
|
-
|
1916
|
+
let document;
|
1917
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1918
|
+
const isCreate = fp.isNil(id);
|
1919
|
+
if (isCreate) {
|
1920
|
+
if (permissionChecker2.cannot.create()) {
|
1921
|
+
throw new strapiUtils.errors.ForbiddenError();
|
1922
|
+
}
|
1923
|
+
document = await createDocument(ctx, { populate });
|
1924
|
+
}
|
1925
|
+
const isUpdate = !isCreate;
|
1926
|
+
if (isUpdate) {
|
1927
|
+
document = await documentManager2.findOne(id, model, { populate, locale });
|
1928
|
+
if (!document) {
|
1929
|
+
throw new strapiUtils.errors.NotFoundError("Document not found");
|
1930
|
+
}
|
1931
|
+
if (permissionChecker2.can.update(document)) {
|
1932
|
+
await updateDocument(ctx);
|
1933
|
+
}
|
1934
|
+
}
|
1795
1935
|
if (permissionChecker2.cannot.publish(document)) {
|
1796
1936
|
throw new strapiUtils.errors.ForbiddenError();
|
1797
1937
|
}
|
1798
|
-
const
|
1799
|
-
return documentManager2.publish(document.documentId, model, {
|
1938
|
+
const publishResult = await documentManager2.publish(document.documentId, model, {
|
1800
1939
|
locale
|
1801
1940
|
// TODO: Allow setting creator fields on publish
|
1802
1941
|
// data: setCreatorFields({ user, isEdition: true })({}),
|
1803
1942
|
});
|
1943
|
+
if (!publishResult || publishResult.length === 0) {
|
1944
|
+
throw new strapiUtils.errors.NotFoundError("Document not found or already published.");
|
1945
|
+
}
|
1946
|
+
return publishResult[0];
|
1804
1947
|
});
|
1805
1948
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
1806
|
-
ctx.body = await
|
1949
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1807
1950
|
},
|
1808
1951
|
async bulkPublish(ctx) {
|
1809
1952
|
const { userAbility } = ctx.state;
|
1810
1953
|
const { model } = ctx.params;
|
1811
1954
|
const { body } = ctx.request;
|
1812
|
-
const {
|
1955
|
+
const { documentIds } = body;
|
1813
1956
|
await validateBulkActionInput(body);
|
1814
1957
|
const documentManager2 = getService$1("document-manager");
|
1815
1958
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
@@ -1818,8 +1961,13 @@ const collectionTypes = {
|
|
1818
1961
|
}
|
1819
1962
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1820
1963
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1821
|
-
const
|
1822
|
-
|
1964
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
1965
|
+
allowMultipleLocales: true
|
1966
|
+
});
|
1967
|
+
const entityPromises = documentIds.map(
|
1968
|
+
(documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
|
1969
|
+
);
|
1970
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1823
1971
|
for (const entity of entities) {
|
1824
1972
|
if (!entity) {
|
1825
1973
|
return ctx.notFound();
|
@@ -1828,24 +1976,27 @@ const collectionTypes = {
|
|
1828
1976
|
return ctx.forbidden();
|
1829
1977
|
}
|
1830
1978
|
}
|
1831
|
-
const
|
1979
|
+
const count = await documentManager2.publishMany(model, documentIds, locale);
|
1832
1980
|
ctx.body = { count };
|
1833
1981
|
},
|
1834
1982
|
async bulkUnpublish(ctx) {
|
1835
1983
|
const { userAbility } = ctx.state;
|
1836
1984
|
const { model } = ctx.params;
|
1837
1985
|
const { body } = ctx.request;
|
1838
|
-
const {
|
1986
|
+
const { documentIds } = body;
|
1839
1987
|
await validateBulkActionInput(body);
|
1840
1988
|
const documentManager2 = getService$1("document-manager");
|
1841
1989
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1842
1990
|
if (permissionChecker2.cannot.unpublish()) {
|
1843
1991
|
return ctx.forbidden();
|
1844
1992
|
}
|
1845
|
-
const
|
1846
|
-
|
1847
|
-
|
1848
|
-
const
|
1993
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
1994
|
+
allowMultipleLocales: true
|
1995
|
+
});
|
1996
|
+
const entityPromises = documentIds.map(
|
1997
|
+
(documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
|
1998
|
+
);
|
1999
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1849
2000
|
for (const entity of entities) {
|
1850
2001
|
if (!entity) {
|
1851
2002
|
return ctx.notFound();
|
@@ -1854,7 +2005,8 @@ const collectionTypes = {
|
|
1854
2005
|
return ctx.forbidden();
|
1855
2006
|
}
|
1856
2007
|
}
|
1857
|
-
const
|
2008
|
+
const entitiesIds = entities.map((document) => document.documentId);
|
2009
|
+
const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
|
1858
2010
|
ctx.body = { count };
|
1859
2011
|
},
|
1860
2012
|
async unpublish(ctx) {
|
@@ -1864,7 +2016,6 @@ const collectionTypes = {
|
|
1864
2016
|
body: { discardDraft, ...body }
|
1865
2017
|
} = ctx.request;
|
1866
2018
|
const documentManager2 = getService$1("document-manager");
|
1867
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1868
2019
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1869
2020
|
if (permissionChecker2.cannot.unpublish()) {
|
1870
2021
|
return ctx.forbidden();
|
@@ -1874,7 +2025,7 @@ const collectionTypes = {
|
|
1874
2025
|
}
|
1875
2026
|
const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
|
1876
2027
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1877
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2028
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1878
2029
|
const document = await documentManager2.findOne(id, model, {
|
1879
2030
|
populate,
|
1880
2031
|
locale,
|
@@ -1896,7 +2047,7 @@ const collectionTypes = {
|
|
1896
2047
|
ctx.body = await strapiUtils.async.pipe(
|
1897
2048
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
1898
2049
|
permissionChecker2.sanitizeOutput,
|
1899
|
-
(document2) =>
|
2050
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1900
2051
|
)(document);
|
1901
2052
|
});
|
1902
2053
|
},
|
@@ -1905,14 +2056,13 @@ const collectionTypes = {
|
|
1905
2056
|
const { id, model } = ctx.params;
|
1906
2057
|
const { body } = ctx.request;
|
1907
2058
|
const documentManager2 = getService$1("document-manager");
|
1908
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1909
2059
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1910
2060
|
if (permissionChecker2.cannot.discard()) {
|
1911
2061
|
return ctx.forbidden();
|
1912
2062
|
}
|
1913
2063
|
const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
|
1914
2064
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1915
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2065
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1916
2066
|
const document = await documentManager2.findOne(id, model, {
|
1917
2067
|
populate,
|
1918
2068
|
locale,
|
@@ -1927,14 +2077,14 @@ const collectionTypes = {
|
|
1927
2077
|
ctx.body = await strapiUtils.async.pipe(
|
1928
2078
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
1929
2079
|
permissionChecker2.sanitizeOutput,
|
1930
|
-
(document2) =>
|
2080
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1931
2081
|
)(document);
|
1932
2082
|
},
|
1933
2083
|
async bulkDelete(ctx) {
|
1934
2084
|
const { userAbility } = ctx.state;
|
1935
2085
|
const { model } = ctx.params;
|
1936
2086
|
const { query, body } = ctx.request;
|
1937
|
-
const {
|
2087
|
+
const { documentIds } = body;
|
1938
2088
|
await validateBulkActionInput(body);
|
1939
2089
|
const documentManager2 = getService$1("document-manager");
|
1940
2090
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
@@ -1942,14 +2092,22 @@ const collectionTypes = {
|
|
1942
2092
|
return ctx.forbidden();
|
1943
2093
|
}
|
1944
2094
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
1945
|
-
const
|
1946
|
-
const
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
2095
|
+
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2096
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2097
|
+
const documentLocales = await documentManager2.findLocales(documentIds, model, {
|
2098
|
+
populate,
|
2099
|
+
locale
|
2100
|
+
});
|
2101
|
+
if (documentLocales.length === 0) {
|
2102
|
+
return ctx.notFound();
|
2103
|
+
}
|
2104
|
+
for (const document of documentLocales) {
|
2105
|
+
if (permissionChecker2.cannot.delete(document)) {
|
2106
|
+
return ctx.forbidden();
|
1950
2107
|
}
|
1951
|
-
}
|
1952
|
-
const
|
2108
|
+
}
|
2109
|
+
const localeDocumentsIds = documentLocales.map((document) => document.documentId);
|
2110
|
+
const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
|
1953
2111
|
ctx.body = { count };
|
1954
2112
|
},
|
1955
2113
|
async countDraftRelations(ctx) {
|
@@ -1962,7 +2120,7 @@ const collectionTypes = {
|
|
1962
2120
|
}
|
1963
2121
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1964
2122
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1965
|
-
const { locale, status
|
2123
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1966
2124
|
const entity = await documentManager2.findOne(id, model, { populate, locale, status });
|
1967
2125
|
if (!entity) {
|
1968
2126
|
return ctx.notFound();
|
@@ -1977,7 +2135,7 @@ const collectionTypes = {
|
|
1977
2135
|
},
|
1978
2136
|
async countManyEntriesDraftRelations(ctx) {
|
1979
2137
|
const { userAbility } = ctx.state;
|
1980
|
-
const ids = ctx.request.query.
|
2138
|
+
const ids = ctx.request.query.documentIds;
|
1981
2139
|
const locale = ctx.request.query.locale;
|
1982
2140
|
const { model } = ctx.params;
|
1983
2141
|
const documentManager2 = getService$1("document-manager");
|
@@ -1985,16 +2143,16 @@ const collectionTypes = {
|
|
1985
2143
|
if (permissionChecker2.cannot.read()) {
|
1986
2144
|
return ctx.forbidden();
|
1987
2145
|
}
|
1988
|
-
const
|
2146
|
+
const documents = await documentManager2.findMany(
|
1989
2147
|
{
|
1990
2148
|
filters: {
|
1991
|
-
|
2149
|
+
documentId: ids
|
1992
2150
|
},
|
1993
2151
|
locale
|
1994
2152
|
},
|
1995
2153
|
model
|
1996
2154
|
);
|
1997
|
-
if (!
|
2155
|
+
if (!documents) {
|
1998
2156
|
return ctx.notFound();
|
1999
2157
|
}
|
2000
2158
|
const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
|
@@ -2185,20 +2343,13 @@ const sanitizeMainField = (model, mainField, userAbility) => {
|
|
2185
2343
|
userAbility,
|
2186
2344
|
model: model.uid
|
2187
2345
|
});
|
2188
|
-
|
2346
|
+
const isMainFieldListable = isListable(model, mainField);
|
2347
|
+
const canReadMainField = permissionChecker2.can.read(null, mainField);
|
2348
|
+
if (!isMainFieldListable || !canReadMainField) {
|
2189
2349
|
return "id";
|
2190
2350
|
}
|
2191
|
-
if (
|
2192
|
-
|
2193
|
-
const userPermissionChecker = getService$1("permission-checker").create({
|
2194
|
-
userAbility,
|
2195
|
-
model: "plugin::users-permissions.user"
|
2196
|
-
});
|
2197
|
-
if (userPermissionChecker.can.read()) {
|
2198
|
-
return "name";
|
2199
|
-
}
|
2200
|
-
}
|
2201
|
-
return "id";
|
2351
|
+
if (model.uid === "plugin::users-permissions.role") {
|
2352
|
+
return "name";
|
2202
2353
|
}
|
2203
2354
|
return mainField;
|
2204
2355
|
};
|
@@ -2398,8 +2549,9 @@ const relations = {
|
|
2398
2549
|
} else {
|
2399
2550
|
where.id = id;
|
2400
2551
|
}
|
2401
|
-
|
2402
|
-
|
2552
|
+
const publishedAt = getPublishedAtClause(status, targetUid);
|
2553
|
+
if (!fp.isEmpty(publishedAt)) {
|
2554
|
+
where[`${alias}.published_at`] = publishedAt;
|
2403
2555
|
}
|
2404
2556
|
if (filterByLocale) {
|
2405
2557
|
where[`${alias}.locale`] = locale;
|
@@ -2456,9 +2608,7 @@ const relations = {
|
|
2456
2608
|
addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
|
2457
2609
|
const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
|
2458
2610
|
...strapi.get("query-params").transform(targetUid, permissionQuery),
|
2459
|
-
ordering: "desc"
|
2460
|
-
page: ctx.request.query.page,
|
2461
|
-
pageSize: ctx.request.query.pageSize
|
2611
|
+
ordering: "desc"
|
2462
2612
|
});
|
2463
2613
|
const relationsUnion = fp.uniqBy("id", fp.concat(sanitizedRes.results, res.results));
|
2464
2614
|
ctx.body = {
|
@@ -2490,7 +2640,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
|
|
2490
2640
|
throw new strapiUtils.errors.ForbiddenError();
|
2491
2641
|
}
|
2492
2642
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
|
2493
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2643
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2494
2644
|
const [documentVersion, otherDocumentVersion] = await Promise.all([
|
2495
2645
|
findDocument(sanitizedQuery, model, { locale, status: "draft" }),
|
2496
2646
|
// Find the first document to check if it exists
|
@@ -2527,12 +2677,11 @@ const singleTypes = {
|
|
2527
2677
|
const { model } = ctx.params;
|
2528
2678
|
const { query = {} } = ctx.request;
|
2529
2679
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2530
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2531
2680
|
if (permissionChecker2.cannot.read()) {
|
2532
2681
|
return ctx.forbidden();
|
2533
2682
|
}
|
2534
2683
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
2535
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
2684
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
2536
2685
|
const version = await findDocument(permissionQuery, model, { locale, status });
|
2537
2686
|
if (!version) {
|
2538
2687
|
if (permissionChecker2.cannot.create()) {
|
@@ -2542,8 +2691,10 @@ const singleTypes = {
|
|
2542
2691
|
if (!document) {
|
2543
2692
|
return ctx.notFound();
|
2544
2693
|
}
|
2545
|
-
const { meta } = await
|
2694
|
+
const { meta } = await formatDocumentWithMetadata(
|
2695
|
+
permissionChecker2,
|
2546
2696
|
model,
|
2697
|
+
// @ts-expect-error - fix types
|
2547
2698
|
{ id: document.documentId, locale, publishedAt: null },
|
2548
2699
|
{ availableLocales: true, availableStatus: false }
|
2549
2700
|
);
|
@@ -2554,16 +2705,15 @@ const singleTypes = {
|
|
2554
2705
|
return ctx.forbidden();
|
2555
2706
|
}
|
2556
2707
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
2557
|
-
ctx.body = await
|
2708
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2558
2709
|
},
|
2559
2710
|
async createOrUpdate(ctx) {
|
2560
2711
|
const { userAbility } = ctx.state;
|
2561
2712
|
const { model } = ctx.params;
|
2562
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2563
2713
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2564
2714
|
const document = await createOrUpdateDocument(ctx);
|
2565
2715
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
2566
|
-
ctx.body = await
|
2716
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2567
2717
|
},
|
2568
2718
|
async delete(ctx) {
|
2569
2719
|
const { userAbility } = ctx.state;
|
@@ -2576,7 +2726,7 @@ const singleTypes = {
|
|
2576
2726
|
}
|
2577
2727
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
2578
2728
|
const populate = await buildPopulateFromQuery(sanitizedQuery, model);
|
2579
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2729
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2580
2730
|
const documentLocales = await documentManager2.findLocales(void 0, model, {
|
2581
2731
|
populate,
|
2582
2732
|
locale
|
@@ -2599,7 +2749,6 @@ const singleTypes = {
|
|
2599
2749
|
const { model } = ctx.params;
|
2600
2750
|
const { query = {} } = ctx.request;
|
2601
2751
|
const documentManager2 = getService$1("document-manager");
|
2602
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2603
2752
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2604
2753
|
if (permissionChecker2.cannot.publish()) {
|
2605
2754
|
return ctx.forbidden();
|
@@ -2614,11 +2763,12 @@ const singleTypes = {
|
|
2614
2763
|
if (permissionChecker2.cannot.publish(document)) {
|
2615
2764
|
throw new strapiUtils.errors.ForbiddenError();
|
2616
2765
|
}
|
2617
|
-
const { locale } = getDocumentLocaleAndStatus(document);
|
2618
|
-
|
2766
|
+
const { locale } = await getDocumentLocaleAndStatus(document, model);
|
2767
|
+
const publishResult = await documentManager2.publish(document.documentId, model, { locale });
|
2768
|
+
return publishResult.at(0);
|
2619
2769
|
});
|
2620
2770
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
2621
|
-
ctx.body = await
|
2771
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2622
2772
|
},
|
2623
2773
|
async unpublish(ctx) {
|
2624
2774
|
const { userAbility } = ctx.state;
|
@@ -2628,7 +2778,6 @@ const singleTypes = {
|
|
2628
2778
|
query = {}
|
2629
2779
|
} = ctx.request;
|
2630
2780
|
const documentManager2 = getService$1("document-manager");
|
2631
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2632
2781
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2633
2782
|
if (permissionChecker2.cannot.unpublish()) {
|
2634
2783
|
return ctx.forbidden();
|
@@ -2637,7 +2786,7 @@ const singleTypes = {
|
|
2637
2786
|
return ctx.forbidden();
|
2638
2787
|
}
|
2639
2788
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
|
2640
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2789
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2641
2790
|
const document = await findDocument(sanitizedQuery, model, { locale });
|
2642
2791
|
if (!document) {
|
2643
2792
|
return ctx.notFound();
|
@@ -2655,7 +2804,7 @@ const singleTypes = {
|
|
2655
2804
|
ctx.body = await strapiUtils.async.pipe(
|
2656
2805
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
2657
2806
|
permissionChecker2.sanitizeOutput,
|
2658
|
-
(document2) =>
|
2807
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2659
2808
|
)(document);
|
2660
2809
|
});
|
2661
2810
|
},
|
@@ -2664,13 +2813,12 @@ const singleTypes = {
|
|
2664
2813
|
const { model } = ctx.params;
|
2665
2814
|
const { body, query = {} } = ctx.request;
|
2666
2815
|
const documentManager2 = getService$1("document-manager");
|
2667
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2668
2816
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2669
2817
|
if (permissionChecker2.cannot.discard()) {
|
2670
2818
|
return ctx.forbidden();
|
2671
2819
|
}
|
2672
2820
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
|
2673
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2821
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2674
2822
|
const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
|
2675
2823
|
if (!document) {
|
2676
2824
|
return ctx.notFound();
|
@@ -2681,7 +2829,7 @@ const singleTypes = {
|
|
2681
2829
|
ctx.body = await strapiUtils.async.pipe(
|
2682
2830
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
2683
2831
|
permissionChecker2.sanitizeOutput,
|
2684
|
-
(document2) =>
|
2832
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2685
2833
|
)(document);
|
2686
2834
|
},
|
2687
2835
|
async countDraftRelations(ctx) {
|
@@ -2690,7 +2838,7 @@ const singleTypes = {
|
|
2690
2838
|
const { query } = ctx.request;
|
2691
2839
|
const documentManager2 = getService$1("document-manager");
|
2692
2840
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2693
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2841
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2694
2842
|
if (permissionChecker2.cannot.read()) {
|
2695
2843
|
return ctx.forbidden();
|
2696
2844
|
}
|
@@ -2711,7 +2859,7 @@ const uid$1 = {
|
|
2711
2859
|
async generateUID(ctx) {
|
2712
2860
|
const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
|
2713
2861
|
const { query = {} } = ctx.request;
|
2714
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2862
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2715
2863
|
await validateUIDField(contentTypeUID, field);
|
2716
2864
|
const uidService = getService$1("uid");
|
2717
2865
|
ctx.body = {
|
@@ -2723,7 +2871,7 @@ const uid$1 = {
|
|
2723
2871
|
ctx.request.body
|
2724
2872
|
);
|
2725
2873
|
const { query = {} } = ctx.request;
|
2726
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2874
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2727
2875
|
await validateUIDField(contentTypeUID, field);
|
2728
2876
|
const uidService = getService$1("uid");
|
2729
2877
|
const isAvailable = await uidService.checkUIDAvailability({
|
@@ -3366,12 +3514,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
|
|
3366
3514
|
ability: userAbility,
|
3367
3515
|
model
|
3368
3516
|
});
|
3369
|
-
const
|
3517
|
+
const { actionProvider } = strapi2.service("admin::permission");
|
3518
|
+
const toSubject = (entity) => {
|
3519
|
+
return entity ? permissionsManager.toSubject(entity, model) : model;
|
3520
|
+
};
|
3370
3521
|
const can = (action, entity, field) => {
|
3371
|
-
|
3522
|
+
const subject = toSubject(entity);
|
3523
|
+
const aliases = actionProvider.unstable_aliases(action, model);
|
3524
|
+
return (
|
3525
|
+
// Test the original action to see if it passes
|
3526
|
+
userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
|
3527
|
+
aliases.some((alias) => userAbility.can(alias, subject, field))
|
3528
|
+
);
|
3372
3529
|
};
|
3373
3530
|
const cannot = (action, entity, field) => {
|
3374
|
-
|
3531
|
+
const subject = toSubject(entity);
|
3532
|
+
const aliases = actionProvider.unstable_aliases(action, model);
|
3533
|
+
return (
|
3534
|
+
// Test both the original action
|
3535
|
+
userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
|
3536
|
+
aliases.every((alias) => userAbility.cannot(alias, subject, field))
|
3537
|
+
);
|
3375
3538
|
};
|
3376
3539
|
const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
|
3377
3540
|
return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
|
@@ -3514,7 +3677,7 @@ const permission = ({ strapi: strapi2 }) => ({
|
|
3514
3677
|
await strapi2.service("admin::permission").actionProvider.registerMany(actions);
|
3515
3678
|
}
|
3516
3679
|
});
|
3517
|
-
const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils__default.default.contentTypes;
|
3680
|
+
const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils__default.default.contentTypes;
|
3518
3681
|
const { isAnyToMany } = strapiUtils__default.default.relations;
|
3519
3682
|
const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils__default.default.contentTypes.constants;
|
3520
3683
|
const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
|
@@ -3605,6 +3768,42 @@ const getDeepPopulate = (uid2, {
|
|
3605
3768
|
{}
|
3606
3769
|
);
|
3607
3770
|
};
|
3771
|
+
const getValidatableFieldsPopulate = (uid2, {
|
3772
|
+
initialPopulate = {},
|
3773
|
+
countMany = false,
|
3774
|
+
countOne = false,
|
3775
|
+
maxLevel = Infinity
|
3776
|
+
} = {}, level = 1) => {
|
3777
|
+
if (level > maxLevel) {
|
3778
|
+
return {};
|
3779
|
+
}
|
3780
|
+
const model = strapi.getModel(uid2);
|
3781
|
+
return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
|
3782
|
+
if (!getDoesAttributeRequireValidation(attribute)) {
|
3783
|
+
return populateAcc;
|
3784
|
+
}
|
3785
|
+
if (isScalarAttribute(attribute)) {
|
3786
|
+
return fp.merge(populateAcc, {
|
3787
|
+
[attributeName]: true
|
3788
|
+
});
|
3789
|
+
}
|
3790
|
+
return fp.merge(
|
3791
|
+
populateAcc,
|
3792
|
+
getPopulateFor(
|
3793
|
+
attributeName,
|
3794
|
+
model,
|
3795
|
+
{
|
3796
|
+
// @ts-expect-error - improve types
|
3797
|
+
initialPopulate: initialPopulate?.[attributeName],
|
3798
|
+
countMany,
|
3799
|
+
countOne,
|
3800
|
+
maxLevel
|
3801
|
+
},
|
3802
|
+
level
|
3803
|
+
)
|
3804
|
+
);
|
3805
|
+
}, {});
|
3806
|
+
};
|
3608
3807
|
const getDeepPopulateDraftCount = (uid2) => {
|
3609
3808
|
const model = strapi.getModel(uid2);
|
3610
3809
|
let hasRelations = false;
|
@@ -3612,6 +3811,10 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3612
3811
|
const attribute = model.attributes[attributeName];
|
3613
3812
|
switch (attribute.type) {
|
3614
3813
|
case "relation": {
|
3814
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
3815
|
+
if (isMorphRelation) {
|
3816
|
+
break;
|
3817
|
+
}
|
3615
3818
|
if (isVisibleAttribute$1(model, attributeName)) {
|
3616
3819
|
populateAcc[attributeName] = {
|
3617
3820
|
count: true,
|
@@ -3626,22 +3829,24 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3626
3829
|
attribute.component
|
3627
3830
|
);
|
3628
3831
|
if (childHasRelations) {
|
3629
|
-
populateAcc[attributeName] = {
|
3832
|
+
populateAcc[attributeName] = {
|
3833
|
+
populate: populate2
|
3834
|
+
};
|
3630
3835
|
hasRelations = true;
|
3631
3836
|
}
|
3632
3837
|
break;
|
3633
3838
|
}
|
3634
3839
|
case "dynamiczone": {
|
3635
|
-
const
|
3636
|
-
const { populate:
|
3637
|
-
if (
|
3840
|
+
const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
|
3841
|
+
const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
|
3842
|
+
if (componentHasRelations) {
|
3638
3843
|
hasRelations = true;
|
3639
|
-
return
|
3844
|
+
return { ...acc, [componentUID]: { populate: componentPopulate } };
|
3640
3845
|
}
|
3641
3846
|
return acc;
|
3642
3847
|
}, {});
|
3643
|
-
if (!fp.isEmpty(
|
3644
|
-
populateAcc[attributeName] = {
|
3848
|
+
if (!fp.isEmpty(dzPopulateFragment)) {
|
3849
|
+
populateAcc[attributeName] = { on: dzPopulateFragment };
|
3645
3850
|
}
|
3646
3851
|
break;
|
3647
3852
|
}
|
@@ -3833,41 +4038,70 @@ const AVAILABLE_STATUS_FIELDS = [
|
|
3833
4038
|
"updatedBy",
|
3834
4039
|
"status"
|
3835
4040
|
];
|
3836
|
-
const AVAILABLE_LOCALES_FIELDS = [
|
4041
|
+
const AVAILABLE_LOCALES_FIELDS = [
|
4042
|
+
"id",
|
4043
|
+
"locale",
|
4044
|
+
"updatedAt",
|
4045
|
+
"createdAt",
|
4046
|
+
"status",
|
4047
|
+
"publishedAt",
|
4048
|
+
"documentId"
|
4049
|
+
];
|
3837
4050
|
const CONTENT_MANAGER_STATUS = {
|
3838
4051
|
PUBLISHED: "published",
|
3839
4052
|
DRAFT: "draft",
|
3840
4053
|
MODIFIED: "modified"
|
3841
4054
|
};
|
3842
|
-
const
|
3843
|
-
if (!
|
4055
|
+
const getIsVersionLatestModification = (version, otherVersion) => {
|
4056
|
+
if (!version || !version.updatedAt) {
|
3844
4057
|
return false;
|
3845
4058
|
}
|
3846
|
-
const
|
3847
|
-
const
|
3848
|
-
|
3849
|
-
return difference <= threshold;
|
4059
|
+
const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
|
4060
|
+
const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
|
4061
|
+
return versionUpdatedAt > otherUpdatedAt;
|
3850
4062
|
};
|
3851
4063
|
const documentMetadata = ({ strapi: strapi2 }) => ({
|
3852
4064
|
/**
|
3853
4065
|
* Returns available locales of a document for the current status
|
3854
4066
|
*/
|
3855
|
-
getAvailableLocales(uid2, version, allVersions) {
|
4067
|
+
async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
|
3856
4068
|
const versionsByLocale = fp.groupBy("locale", allVersions);
|
3857
4069
|
delete versionsByLocale[version.locale];
|
3858
|
-
|
3859
|
-
|
3860
|
-
|
4070
|
+
const model = strapi2.getModel(uid2);
|
4071
|
+
const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
|
4072
|
+
const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
|
4073
|
+
({ key }, { remove }) => {
|
4074
|
+
if (keysToKeep.includes(key)) {
|
4075
|
+
return;
|
4076
|
+
}
|
4077
|
+
remove(key);
|
4078
|
+
},
|
4079
|
+
{ schema: model, getModel: strapi2.getModel.bind(strapi2) },
|
4080
|
+
// @ts-expect-error fix types DocumentVersion incompatible with Data
|
4081
|
+
localeVersion
|
4082
|
+
);
|
4083
|
+
const mappingResult = await strapiUtils.async.map(
|
4084
|
+
Object.values(versionsByLocale),
|
4085
|
+
async (localeVersions) => {
|
4086
|
+
const mappedLocaleVersions = await strapiUtils.async.map(
|
4087
|
+
localeVersions,
|
4088
|
+
traversalFunction
|
4089
|
+
);
|
4090
|
+
if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
|
4091
|
+
return mappedLocaleVersions[0];
|
4092
|
+
}
|
4093
|
+
const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
|
4094
|
+
const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
|
4095
|
+
if (!draftVersion) {
|
4096
|
+
return;
|
4097
|
+
}
|
4098
|
+
return {
|
4099
|
+
...draftVersion,
|
4100
|
+
status: this.getStatus(draftVersion, otherVersions)
|
4101
|
+
};
|
3861
4102
|
}
|
3862
|
-
|
3863
|
-
|
3864
|
-
if (!draftVersion)
|
3865
|
-
return;
|
3866
|
-
return {
|
3867
|
-
...fp.pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
|
3868
|
-
status: this.getStatus(draftVersion, otherVersions)
|
3869
|
-
};
|
3870
|
-
}).filter(Boolean);
|
4103
|
+
);
|
4104
|
+
return mappingResult.filter(Boolean);
|
3871
4105
|
},
|
3872
4106
|
/**
|
3873
4107
|
* Returns available status of a document for the current locale
|
@@ -3905,26 +4139,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3905
4139
|
});
|
3906
4140
|
},
|
3907
4141
|
getStatus(version, otherDocumentStatuses) {
|
3908
|
-
|
3909
|
-
|
3910
|
-
|
4142
|
+
let draftVersion;
|
4143
|
+
let publishedVersion;
|
4144
|
+
if (version.publishedAt) {
|
4145
|
+
publishedVersion = version;
|
4146
|
+
} else {
|
4147
|
+
draftVersion = version;
|
3911
4148
|
}
|
3912
|
-
|
3913
|
-
|
3914
|
-
|
3915
|
-
|
3916
|
-
|
4149
|
+
const otherVersion = otherDocumentStatuses?.at(0);
|
4150
|
+
if (otherVersion?.publishedAt) {
|
4151
|
+
publishedVersion = otherVersion;
|
4152
|
+
} else if (otherVersion) {
|
4153
|
+
draftVersion = otherVersion;
|
3917
4154
|
}
|
3918
|
-
if (
|
4155
|
+
if (!draftVersion)
|
3919
4156
|
return CONTENT_MANAGER_STATUS.PUBLISHED;
|
3920
|
-
|
3921
|
-
|
4157
|
+
if (!publishedVersion)
|
4158
|
+
return CONTENT_MANAGER_STATUS.DRAFT;
|
4159
|
+
const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
|
4160
|
+
return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
|
3922
4161
|
},
|
4162
|
+
// TODO is it necessary to return metadata on every page of the CM
|
4163
|
+
// We could refactor this so the locales are only loaded when they're
|
4164
|
+
// needed. e.g. in the bulk locale action modal.
|
3923
4165
|
async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
|
4166
|
+
const populate = getValidatableFieldsPopulate(uid2);
|
3924
4167
|
const versions = await strapi2.db.query(uid2).findMany({
|
3925
4168
|
where: { documentId: version.documentId },
|
3926
|
-
select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
|
3927
4169
|
populate: {
|
4170
|
+
// Populate only fields that require validation for bulk locale actions
|
4171
|
+
...populate,
|
4172
|
+
// NOTE: creator fields are selected in this way to avoid exposing sensitive data
|
3928
4173
|
createdBy: {
|
3929
4174
|
select: ["id", "firstname", "lastname", "email"]
|
3930
4175
|
},
|
@@ -3933,7 +4178,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3933
4178
|
}
|
3934
4179
|
}
|
3935
4180
|
});
|
3936
|
-
const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
|
4181
|
+
const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
|
3937
4182
|
const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
|
3938
4183
|
return {
|
3939
4184
|
availableLocales: availableLocalesResult,
|
@@ -3946,8 +4191,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3946
4191
|
* - Available status of the document for the current locale
|
3947
4192
|
*/
|
3948
4193
|
async formatDocumentWithMetadata(uid2, document, opts = {}) {
|
3949
|
-
if (!document)
|
3950
|
-
return
|
4194
|
+
if (!document) {
|
4195
|
+
return {
|
4196
|
+
data: document,
|
4197
|
+
meta: {
|
4198
|
+
availableLocales: [],
|
4199
|
+
availableStatus: []
|
4200
|
+
}
|
4201
|
+
};
|
4202
|
+
}
|
3951
4203
|
const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
|
3952
4204
|
if (!hasDraftAndPublish) {
|
3953
4205
|
opts.availableStatus = false;
|
@@ -3997,26 +4249,9 @@ const sumDraftCounts = (entity, uid2) => {
|
|
3997
4249
|
}, 0);
|
3998
4250
|
};
|
3999
4251
|
const { ApplicationError } = strapiUtils.errors;
|
4000
|
-
const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
|
4001
4252
|
const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
|
4002
4253
|
const omitPublishedAtField = fp.omit(PUBLISHED_AT_ATTRIBUTE);
|
4003
4254
|
const omitIdField = fp.omit("id");
|
4004
|
-
const emitEvent = async (uid2, event, document) => {
|
4005
|
-
const modelDef = strapi.getModel(uid2);
|
4006
|
-
const sanitizedDocument = await strapiUtils.sanitize.sanitizers.defaultSanitizeOutput(
|
4007
|
-
{
|
4008
|
-
schema: modelDef,
|
4009
|
-
getModel(uid22) {
|
4010
|
-
return strapi.getModel(uid22);
|
4011
|
-
}
|
4012
|
-
},
|
4013
|
-
document
|
4014
|
-
);
|
4015
|
-
strapi.eventHub.emit(event, {
|
4016
|
-
model: modelDef.modelName,
|
4017
|
-
entry: sanitizedDocument
|
4018
|
-
});
|
4019
|
-
};
|
4020
4255
|
const documentManager = ({ strapi: strapi2 }) => {
|
4021
4256
|
return {
|
4022
4257
|
async findOne(id, uid2, opts = {}) {
|
@@ -4035,6 +4270,9 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4035
4270
|
} else if (opts.locale && opts.locale !== "*") {
|
4036
4271
|
where.locale = opts.locale;
|
4037
4272
|
}
|
4273
|
+
if (typeof opts.isPublished === "boolean") {
|
4274
|
+
where.publishedAt = { $notNull: opts.isPublished };
|
4275
|
+
}
|
4038
4276
|
return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
|
4039
4277
|
},
|
4040
4278
|
async findMany(opts, uid2) {
|
@@ -4042,20 +4280,16 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4042
4280
|
return strapi2.documents(uid2).findMany(params);
|
4043
4281
|
},
|
4044
4282
|
async findPage(opts, uid2) {
|
4045
|
-
const
|
4046
|
-
|
4283
|
+
const params = strapiUtils.pagination.withDefaultPagination(opts || {}, {
|
4284
|
+
maxLimit: 1e3
|
4285
|
+
});
|
4047
4286
|
const [documents, total = 0] = await Promise.all([
|
4048
|
-
strapi2.documents(uid2).findMany(
|
4049
|
-
strapi2.documents(uid2).count(
|
4287
|
+
strapi2.documents(uid2).findMany(params),
|
4288
|
+
strapi2.documents(uid2).count(params)
|
4050
4289
|
]);
|
4051
4290
|
return {
|
4052
4291
|
results: documents,
|
4053
|
-
pagination:
|
4054
|
-
page,
|
4055
|
-
pageSize,
|
4056
|
-
pageCount: Math.ceil(total / pageSize),
|
4057
|
-
total
|
4058
|
-
}
|
4292
|
+
pagination: strapiUtils.pagination.transformPagedPaginationInfo(params, total)
|
4059
4293
|
};
|
4060
4294
|
},
|
4061
4295
|
async create(uid2, opts = {}) {
|
@@ -4072,10 +4306,7 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4072
4306
|
async clone(id, body, uid2) {
|
4073
4307
|
const populate = await buildDeepPopulate(uid2);
|
4074
4308
|
const params = {
|
4075
|
-
data:
|
4076
|
-
...omitIdField(body),
|
4077
|
-
[PUBLISHED_AT_ATTRIBUTE]: null
|
4078
|
-
},
|
4309
|
+
data: omitIdField(body),
|
4079
4310
|
populate
|
4080
4311
|
};
|
4081
4312
|
return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
|
@@ -4101,70 +4332,36 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4101
4332
|
return {};
|
4102
4333
|
},
|
4103
4334
|
// FIXME: handle relations
|
4104
|
-
async deleteMany(
|
4105
|
-
const
|
4106
|
-
|
4107
|
-
|
4108
|
-
}
|
4109
|
-
return { count: docs.length };
|
4335
|
+
async deleteMany(documentIds, uid2, opts = {}) {
|
4336
|
+
const deletedEntries = await strapi2.db.transaction(async () => {
|
4337
|
+
return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
|
4338
|
+
});
|
4339
|
+
return { count: deletedEntries.length };
|
4110
4340
|
},
|
4111
4341
|
async publish(id, uid2, opts = {}) {
|
4112
4342
|
const populate = await buildDeepPopulate(uid2);
|
4113
4343
|
const params = { ...opts, populate };
|
4114
|
-
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries
|
4344
|
+
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
|
4115
4345
|
},
|
4116
|
-
async publishMany(
|
4117
|
-
|
4118
|
-
|
4119
|
-
|
4120
|
-
|
4121
|
-
|
4122
|
-
|
4123
|
-
strapi2.getModel(uid2),
|
4124
|
-
document,
|
4125
|
-
void 0,
|
4126
|
-
// @ts-expect-error - FIXME: entity here is unnecessary
|
4127
|
-
document
|
4128
|
-
);
|
4129
|
-
})
|
4130
|
-
);
|
4131
|
-
const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
|
4132
|
-
const filters = { id: { $in: entitiesToPublish } };
|
4133
|
-
const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
|
4134
|
-
const populate = await buildDeepPopulate(uid2);
|
4135
|
-
const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
|
4136
|
-
where: filters,
|
4137
|
-
data
|
4138
|
-
});
|
4139
|
-
const publishedEntities = await strapi2.db.query(uid2).findMany({
|
4140
|
-
where: filters,
|
4141
|
-
populate
|
4346
|
+
async publishMany(uid2, documentIds, locale) {
|
4347
|
+
return strapi2.db.transaction(async () => {
|
4348
|
+
const results = await Promise.all(
|
4349
|
+
documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
|
4350
|
+
);
|
4351
|
+
const publishedEntitiesCount = results.flat().filter(Boolean).length;
|
4352
|
+
return publishedEntitiesCount;
|
4142
4353
|
});
|
4143
|
-
await Promise.all(
|
4144
|
-
publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
|
4145
|
-
);
|
4146
|
-
return publishedEntitiesCount;
|
4147
4354
|
},
|
4148
|
-
async unpublishMany(
|
4149
|
-
|
4150
|
-
return
|
4151
|
-
|
4152
|
-
|
4153
|
-
|
4154
|
-
|
4155
|
-
const populate = await buildDeepPopulate(uid2);
|
4156
|
-
const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
|
4157
|
-
where: filters,
|
4158
|
-
data
|
4159
|
-
});
|
4160
|
-
const unpublishedEntities = await strapi2.db.query(uid2).findMany({
|
4161
|
-
where: filters,
|
4162
|
-
populate
|
4355
|
+
async unpublishMany(documentIds, uid2, opts = {}) {
|
4356
|
+
const unpublishedEntries = await strapi2.db.transaction(async () => {
|
4357
|
+
return Promise.all(
|
4358
|
+
documentIds.map(
|
4359
|
+
(id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
|
4360
|
+
)
|
4361
|
+
);
|
4163
4362
|
});
|
4164
|
-
|
4165
|
-
|
4166
|
-
);
|
4167
|
-
return unpublishedEntitiesCount;
|
4363
|
+
const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
|
4364
|
+
return { count: unpublishedEntitiesCount };
|
4168
4365
|
},
|
4169
4366
|
async unpublish(id, uid2, opts = {}) {
|
4170
4367
|
const populate = await buildDeepPopulate(uid2);
|
@@ -4189,16 +4386,20 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4189
4386
|
}
|
4190
4387
|
return sumDraftCounts(document, uid2);
|
4191
4388
|
},
|
4192
|
-
async countManyEntriesDraftRelations(
|
4389
|
+
async countManyEntriesDraftRelations(documentIds, uid2, locale) {
|
4193
4390
|
const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
|
4194
4391
|
if (!hasRelations) {
|
4195
4392
|
return 0;
|
4196
4393
|
}
|
4394
|
+
let localeFilter = {};
|
4395
|
+
if (locale) {
|
4396
|
+
localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
|
4397
|
+
}
|
4197
4398
|
const entities = await strapi2.db.query(uid2).findMany({
|
4198
4399
|
populate,
|
4199
4400
|
where: {
|
4200
|
-
|
4201
|
-
...
|
4401
|
+
documentId: { $in: documentIds },
|
4402
|
+
...localeFilter
|
4202
4403
|
}
|
4203
4404
|
});
|
4204
4405
|
const totalNumberDraftRelations = entities.reduce(
|