Disclaimer!!: This plugin is an early alpha version mainly used for internal testing before a proper release. We do not encourage you to use it in production (but we are working heavily so this could change in near future).
This plugin enables you to versioning Content Types in Strapi v4! 🎉🎉🎉
- Having multiple draft versions✅
- Keeping a history of all changes (with time travel) ✅
- Allowing you to have different published and draft data ✅
Run npm i @notum-cz/strapi-plugin-content-versioning
or use yarn add @notum-cz/strapi-plugin-content-versioning
- Versioning can be enabled in settings of Content Type. Same as localziation plugin.
- You need to have enabled draft/publish system on your content type.
- You need to create/modify file
config/plugins.js
with
module.exports = ({ env }) => ({
"content-versioning": {
enabled: true,
},
});
- (Optional) If you want to override also the Save button to work with this plugin you need to follow the instructions below. ⬇️⬇️
You have to use patch-package to make it work with native Save button. (We are working closely with tthe core team to change this).
- Install
patch-package
npm install patch-package
oryarn add patch-package
- Create folder
patches
in root of your project - Add file
@strapi+admin+4.0.2.patch
with content below ⬇️ - Add the line
"postinstall": "patch-package",
to the scripts section of thepackage.json
- Run
npm run postinstall
diff --git a/node_modules/@strapi/admin/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js b/node_modules/@strapi/admin/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js
index 6701309..393f616 100644
--- a/node_modules/@strapi/admin/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js
+++ b/node_modules/@strapi/admin/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js
@@ -247,9 +247,17 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
replace(redirectionLink);
}, [redirectionLink, replace]);
+
+ const currentContentTypeLayout = get(allLayoutData, ['contentType'], {});
+
+ const hasVersions = useMemo(() => {
+ return get(currentContentTypeLayout, ['pluginOptions', 'versions', 'versioned'], false);
+ }, [currentContentTypeLayout]);
+
+
const onPost = useCallback(
async (body, trackerProperty) => {
- const endPoint = `${getRequestUrl(`collection-types/${slug}`)}${rawQuery}`;
+ const endPoint = hasVersions ? `/content-versioning/${slug}/save` : `${getRequestUrl(`collection-types/${slug}`)}${rawQuery}`;
try {
// Show a loading button in the EditView/Header.js && lock the app => no navigation
@@ -267,7 +275,13 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
// Enable navigation and remove loaders
dispatch(setStatus('resolved'));
- replace(`/content-manager/collectionType/${slug}/${data.id}${rawQuery}`);
+ if (hasVersions) {
+ replace({
+ pathname: `/content-manager/collectionType/${slug}/${data.id}`,
+ });
+ } else {
+ replace(`/content-manager/collectionType/${slug}/${data.id}${rawQuery}`);
+ }
} catch (err) {
trackUsageRef.current('didNotCreateEntry', { error: err, trackerProperty });
displayErrors(err);
@@ -303,14 +317,15 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
const onPut = useCallback(
async (body, trackerProperty) => {
- const endPoint = getRequestUrl(`collection-types/${slug}/${id}`);
+
+ const endPoint = hasVersions ? `/content-versioning/${slug}/save` : getRequestUrl(`collection-types/${slug}/${id}`);
try {
trackUsageRef.current('willEditEntry', trackerProperty);
dispatch(setStatus('submit-pending'));
- const { data } = await axiosInstance.put(endPoint, body);
+ const { data } = hasVersions ? await axiosInstance.post(endPoint, body) : await axiosInstance.put(endPoint, body);
trackUsageRef.current('didEditEntry', { trackerProperty });
toggleNotification({
@@ -321,6 +336,12 @@ const CollectionTypeFormWrapper = ({ allLayoutData, children, slug, id, origin }
dispatch(submitSucceeded(cleanReceivedData(data)));
dispatch(setStatus('resolved'));
+
+ if (hasVersions) {
+ replace({
+ pathname: `/content-manager/collectionType/${slug}/${data.id}`,
+ });
+ }
} catch (err) {
trackUsageRef.current('didNotEditEntry', { error: err, trackerProperty });
displayErrors(err);
diff --git a/node_modules/@strapi/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js b/node_modules/@strapi/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js
index aff6f07..c5d7b87 100644
--- a/node_modules/@strapi/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js
+++ b/node_modules/@strapi/admin/admin/src/content-manager/components/EditViewDataManagerProvider/index.js
@@ -49,6 +49,10 @@ const EditViewDataManagerProvider = ({
return get(currentContentTypeLayout, ['options', 'draftAndPublish'], false);
}, [currentContentTypeLayout]);
+ const hasVersions = useMemo(() => {
+ return get(currentContentTypeLayout, ['pluginOptions', 'versions', 'versioned'], false);
+ }, [currentContentTypeLayout]);
+
const shouldNotRunValidations = useMemo(() => {
return hasDraftAndPublish && !initialData.publishedAt;
}, [hasDraftAndPublish, initialData.publishedAt]);
@@ -515,7 +519,7 @@ const EditViewDataManagerProvider = ({
) : (
<>
<Prompt
- when={!isEqual(modifiedData, initialData)}
+ when={!hasVersions && !isEqual(modifiedData, initialData)}
message={formatMessage({ id: 'global.prompt.unsaved' })}
/>
<form noValidate onSubmit={handleSubmit}>
- ✨ Fix of the "patch-package problem"
- ✨ Extension of functionality also for for single types
- ✨ Autosave
- ✨ Update of the current version without creating new history item
- ✋ ⛔️ Not working with UID and unique fields
We are using GitHub Issues to manage bugs. Please have a look at existing issues before creating a new one.
The main star: Martin Čapek https://github.com/martincapek
Tech problem solver: Tomáš Novotný
Happily created by Notum Technologies - official STRAPI partner and Czech based custom development agency - to share some of our Strapi expertise with the broader open source community.