Jamie Howard 43b9e91c67
feat(i18n): non localized fields (#19720)
* feat(i18n): wip non localized fields

* feat(i18n): use document service middleware for non localized fields

* feat(i18n): support component data in non localized sync

* chore(i18n): cleanup and fix unit tests

* fix(i18n): reintroduce permissions sync

* test(i18n): api test for non localized fields

* feat(i18n): add earth icon to symbolise localised fields (#19774)

* test(i18n): cover publish, unpublish, update and create in api tests

* feat(i18n): ensure non localized fields are populated

* fix(i18n): get right id

* fix(content-manager): doc metadata in non d&p cases

* fix(conent-manager): i18n detection

* fix: pr feedback

* fix(i18n): handle non localized components

* feat(i18n): sync non localized components

* fix(i18n): wip unit test

* feat(i18n): handle relations within non localized components

* feat(i18n): reintroduce FE and fix for repeatables

* chore: lockfile

* chore(i18n): cleanup

* chore(i18n): cleanup

* feat(i18n): match relation locales to source entry during transformation

* fix(i18n): unit tests

* fix(i18n): getNonLocalizedAttributes

* chore(i18n): fix unit tests

* chore(i18n): pr feedback

* chore(i18n): pr feedback

* fix(i18n): unit tests

---------

Co-authored-by: Josh <37798644+joshuaellis@users.noreply.github.com>
2024-04-02 10:08:10 +01:00

161 lines
5.8 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-var-requires */
import type { Core, Data, UID } from '@strapi/types';
import { RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID, ALLOWED_WEBHOOK_EVENTS } from './constants';
import { getEntryValidStatus, getService } from './utils';
export const bootstrap = async ({ strapi }: { strapi: Core.LoadedStrapi }) => {
if (strapi.ee.features.isEnabled('cms-content-releases')) {
const contentTypesWithDraftAndPublish = Object.keys(strapi.contentTypes).filter(
(uid: any) => strapi.contentTypes[uid]?.options?.draftAndPublish
);
// Clean up release-actions when an entry is deleted
strapi.db.lifecycles.subscribe({
models: contentTypesWithDraftAndPublish,
async afterDelete(event) {
try {
const { model, result } = event;
// @ts-expect-error TODO: lifecycles types looks like are not 100% finished
if (model.kind === 'collectionType' && model.options?.draftAndPublish) {
const { id } = result;
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
where: {
actions: {
target_type: model.uid,
target_id: id,
},
},
});
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
where: {
target_type: model.uid,
target_id: id,
},
});
// We update the status of each release after delete the actions
for (const release of releases) {
getService('release', { strapi }).updateReleaseStatus(release.id);
}
}
} catch (error) {
// If an error happens we don't want to block the delete entry flow, but we log the error
strapi.log.error('Error while deleting release actions after entry delete', { error });
}
},
/**
* deleteMany hook doesn't return the deleted entries ids
* so we need to fetch them before deleting the entries to save the ids on our state
*/
async beforeDeleteMany(event) {
const { model, params } = event;
// @ts-expect-error TODO: lifecycles types looks like are not 100% finished
if (model.kind === 'collectionType' && model.options?.draftAndPublish) {
const { where } = params;
const entriesToDelete = await strapi.db
.query(model.uid)
.findMany({ select: ['id'], where });
event.state.entriesToDelete = entriesToDelete;
}
},
/**
* We delete the release actions related to deleted entries
* We make this only after deleteMany is succesfully executed to avoid errors
*/
async afterDeleteMany(event) {
try {
const { model, state } = event;
const entriesToDelete = state.entriesToDelete;
if (entriesToDelete) {
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
where: {
actions: {
target_type: model.uid,
target_id: {
$in: (entriesToDelete as Array<{ id: Data.ID }>).map((entry) => entry.id),
},
},
},
});
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
where: {
target_type: model.uid,
target_id: {
$in: (entriesToDelete as Array<{ id: Data.ID }>).map((entry) => entry.id),
},
},
});
// We update the status of each release after delete the actions
for (const release of releases) {
getService('release', { strapi }).updateReleaseStatus(release.id);
}
}
} catch (error) {
// If an error happens we don't want to block the delete entry flow, but we log the error
strapi.log.error('Error while deleting release actions after entry deleteMany', {
error,
});
}
},
async afterUpdate(event) {
try {
const { model, result } = event;
// @ts-expect-error TODO: lifecycles types looks like are not 100% finished
if (model.kind === 'collectionType' && model.options?.draftAndPublish) {
const isEntryValid = await getEntryValidStatus(model.uid as UID.ContentType, result, {
strapi,
});
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
where: {
target_type: model.uid,
target_id: result.id,
},
data: {
isEntryValid,
},
});
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
where: {
actions: {
target_type: model.uid,
target_id: result.id,
},
},
});
for (const release of releases) {
getService('release', { strapi }).updateReleaseStatus(release.id);
}
}
} catch (error) {
// If an error happens we don't want to block the update entry flow, but we log the error
strapi.log.error('Error while updating release actions after entry update', { error });
}
},
});
getService('scheduling', { strapi })
.syncFromDatabase()
.catch((err: Error) => {
strapi.log.error(
'Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling.'
);
throw err;
});
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
strapi.webhookStore.addAllowedEvent(key, value);
});
}
};