mirror of
https://github.com/strapi/strapi.git
synced 2025-12-05 03:21:22 +00:00
Publish a draft
Signed-off-by: HichamELBSI <elabbassih@gmail.com>
This commit is contained in:
parent
941b381ffb
commit
23ede00fa8
@ -251,6 +251,7 @@
|
||||
"app.utils.add-filter": "Add filter",
|
||||
"app.utils.defaultMessage": " ",
|
||||
"app.utils.delete": "Delete",
|
||||
"app.utils.publish": "Publish",
|
||||
"app.utils.filters": "Filters",
|
||||
"app.utils.placeholder.defaultMessage": " ",
|
||||
"app.utils.select-all": "Select all",
|
||||
|
||||
@ -6,10 +6,11 @@ const getAttributesToDisplay = contentType => {
|
||||
// Sometimes timestamps is false
|
||||
let timestampsArray = Array.isArray(timestamps) ? timestamps : [];
|
||||
const idsAttributes = ['id', '_id']; // For both SQL and mongo
|
||||
const unwritableAttributes = [...idsAttributes, ...timestampsArray, 'published_at'];
|
||||
const schemaAttributes = get(contentType, ['schema', 'attributes'], {});
|
||||
|
||||
return Object.keys(schemaAttributes).reduce((acc, current) => {
|
||||
if (![...idsAttributes, ...timestampsArray].includes(current)) {
|
||||
if (!unwritableAttributes.includes(current)) {
|
||||
acc.push({ ...schemaAttributes[current], attributeName: current });
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ describe('ADMIN | utils | getAttributesToDisplay', () => {
|
||||
description: { type: 'string' },
|
||||
created_at: { type: 'timestamp' },
|
||||
updated_at: { type: 'timestamp' },
|
||||
published_at: { type: 'timestamp' },
|
||||
},
|
||||
options: {
|
||||
timestamps: ['created_at', 'updated_at'],
|
||||
|
||||
@ -24,21 +24,26 @@ const Header = () => {
|
||||
isCreatingEntry,
|
||||
isSingleType,
|
||||
isSubmitting,
|
||||
isPublishing,
|
||||
layout,
|
||||
modifiedData,
|
||||
onPublish,
|
||||
redirectToPreviousPage,
|
||||
resetData,
|
||||
slug,
|
||||
clearData,
|
||||
} = useDataManager();
|
||||
const {
|
||||
allowedActions: { canDelete, canUpdate, canCreate },
|
||||
allowedActions: { canDelete, canUpdate, canCreate, canPublish },
|
||||
} = useEditView();
|
||||
|
||||
const currentContentTypeMainField = useMemo(() => get(layout, ['settings', 'mainField'], 'id'), [
|
||||
layout,
|
||||
]);
|
||||
const currentContentTypeName = useMemo(() => get(layout, ['schema', 'info', 'name']), [layout]);
|
||||
const hasDraftAndPublish = useMemo(() => get(layout, ['schema', 'options', 'draftAndPublish']), [
|
||||
layout,
|
||||
]);
|
||||
const didChangeData = useMemo(() => {
|
||||
return !isEqual(initialData, modifiedData) || (isCreatingEntry && !isEmpty(modifiedData));
|
||||
}, [initialData, isCreatingEntry, modifiedData]);
|
||||
@ -63,22 +68,6 @@ const Header = () => {
|
||||
|
||||
if ((isCreatingEntry && canCreate) || (!isCreatingEntry && canUpdate)) {
|
||||
headerActions = [
|
||||
{
|
||||
disabled: !didChangeData,
|
||||
onClick: () => {
|
||||
toggleWarningCancel();
|
||||
},
|
||||
color: 'cancel',
|
||||
label: formatMessage({
|
||||
id: `${pluginId}.containers.Edit.reset`,
|
||||
}),
|
||||
type: 'button',
|
||||
style: {
|
||||
paddingLeft: 15,
|
||||
paddingRight: 15,
|
||||
fontWeight: 600,
|
||||
},
|
||||
},
|
||||
{
|
||||
disabled: !didChangeData,
|
||||
color: 'success',
|
||||
@ -95,6 +84,23 @@ const Header = () => {
|
||||
];
|
||||
}
|
||||
|
||||
if (hasDraftAndPublish && canPublish) {
|
||||
headerActions.unshift({
|
||||
disabled: didChangeData,
|
||||
label: formatMessage({
|
||||
id: 'app.utils.publish',
|
||||
}),
|
||||
color: 'primary',
|
||||
onClick: onPublish,
|
||||
type: 'button',
|
||||
isLoading: isPublishing,
|
||||
style: {
|
||||
minWidth: 150,
|
||||
fontWeight: 600,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!isCreatingEntry && canDelete) {
|
||||
headerActions.unshift({
|
||||
label: formatMessage({
|
||||
@ -114,14 +120,18 @@ const Header = () => {
|
||||
}
|
||||
|
||||
return headerActions;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
canCreate,
|
||||
canDelete,
|
||||
canUpdate,
|
||||
didChangeData,
|
||||
isCreatingEntry,
|
||||
isSubmitting,
|
||||
canCreate,
|
||||
canUpdate,
|
||||
hasDraftAndPublish,
|
||||
canPublish,
|
||||
canDelete,
|
||||
didChangeData,
|
||||
formatMessage,
|
||||
isSubmitting,
|
||||
isPublishing,
|
||||
]);
|
||||
|
||||
const headerProps = useMemo(() => {
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
||||
import { cloneDeep, get, isEmpty, isEqual, pick, set } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Prompt, Redirect, useParams, useLocation, useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Prompt,
|
||||
Redirect,
|
||||
useParams,
|
||||
useLocation,
|
||||
useHistory,
|
||||
useRouteMatch,
|
||||
} from 'react-router-dom';
|
||||
import {
|
||||
LoadingIndicatorPage,
|
||||
request,
|
||||
@ -52,7 +59,11 @@ const EditViewDataManagerProvider = ({
|
||||
} = reducerState.toJS();
|
||||
const [isCreatingEntry, setIsCreatingEntry] = useState(id === 'create');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isPublishing, setIsPublishing] = useState(false);
|
||||
const currentContentTypeLayout = get(allLayoutData, ['contentType'], {});
|
||||
const {
|
||||
params: { contentType },
|
||||
} = useRouteMatch('/plugins/content-manager/:contentType');
|
||||
// This is used for the readonly mode when updating an entry
|
||||
const allDynamicZoneFields = useMemo(() => {
|
||||
const attributes = get(currentContentTypeLayout, ['schema', 'attributes'], {});
|
||||
@ -406,7 +417,7 @@ const EditViewDataManagerProvider = ({
|
||||
|
||||
try {
|
||||
// Time to actually send the data
|
||||
await request(
|
||||
const res = await request(
|
||||
getRequestUrl(endPoint),
|
||||
{
|
||||
method,
|
||||
@ -426,10 +437,10 @@ const EditViewDataManagerProvider = ({
|
||||
});
|
||||
strapi.notification.success(`${pluginId}.success.record.save`);
|
||||
|
||||
if (isSingleType) {
|
||||
setIsCreatingEntry(false);
|
||||
} else {
|
||||
redirectToPreviousPage();
|
||||
setIsCreatingEntry(false);
|
||||
|
||||
if (!isSingleType) {
|
||||
push(`/plugins/${pluginId}/${contentType}/${slug}/${res.id}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error({ err });
|
||||
@ -474,6 +485,85 @@ const EditViewDataManagerProvider = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handlePublish = async e => {
|
||||
e.preventDefault();
|
||||
|
||||
// Create yup schema
|
||||
const schema = createYupSchema(
|
||||
currentContentTypeLayout,
|
||||
{
|
||||
components: get(allLayoutData, 'components', {}),
|
||||
},
|
||||
isCreatingEntry
|
||||
);
|
||||
|
||||
try {
|
||||
// Validate the form using yup
|
||||
await schema.validate(modifiedData, { abortEarly: false });
|
||||
|
||||
// Show a loading button in the EditView/Header.js
|
||||
setIsPublishing(true);
|
||||
|
||||
try {
|
||||
// Time to actually send the data
|
||||
await request(
|
||||
getRequestUrl(`${slug}/publish/${id}`),
|
||||
{
|
||||
method: 'POST',
|
||||
signal,
|
||||
},
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
setIsPublishing(false);
|
||||
|
||||
dispatch({
|
||||
type: 'PUBLISH_SUCCESS',
|
||||
});
|
||||
strapi.notification.success(`${pluginId}.success.record.publish`);
|
||||
} catch (err) {
|
||||
// ---------- @Soupette Is this error handling still mandatory? ----------
|
||||
// The api error send response.payload.message: 'The error message'.
|
||||
// There isn't : response.payload.message[0].messages[0].id
|
||||
console.error({ err });
|
||||
setIsPublishing(false);
|
||||
|
||||
const error = get(
|
||||
err,
|
||||
['response', 'payload', 'message', '0', 'messages', '0', 'id'],
|
||||
'SERVER ERROR'
|
||||
);
|
||||
|
||||
if (error === 'ValidationError') {
|
||||
const errors = get(err, ['response', 'payload', 'data', '0', 'errors'], {});
|
||||
const formattedErrors = Object.keys(errors).reduce((acc, current) => {
|
||||
acc[current] = { id: errors[current][0] };
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
dispatch({
|
||||
type: 'PUBLISH_ERRORS',
|
||||
errors: formattedErrors,
|
||||
});
|
||||
}
|
||||
|
||||
const errorMessage = get(err, ['response', 'payload', 'message'], 'SERVER ERROR');
|
||||
strapi.notification.error(errorMessage);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error({ err });
|
||||
const errors = getYupInnerErrors(err);
|
||||
setIsSubmitting(false);
|
||||
|
||||
dispatch({
|
||||
type: 'PUBLISH_ERRORS',
|
||||
errors,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const shouldCheckDZErrors = useCallback(
|
||||
dzName => {
|
||||
const doesDZHaveError = Object.keys(formErrors).some(key => key.split('.')[0] === dzName);
|
||||
@ -630,6 +720,7 @@ const EditViewDataManagerProvider = ({
|
||||
isCreatingEntry,
|
||||
isSingleType,
|
||||
isSubmitting,
|
||||
isPublishing,
|
||||
layout: currentContentTypeLayout,
|
||||
modifiedData,
|
||||
moveComponentDown,
|
||||
@ -637,6 +728,7 @@ const EditViewDataManagerProvider = ({
|
||||
moveComponentUp,
|
||||
moveRelation,
|
||||
onChange: handleChange,
|
||||
onPublish: handlePublish,
|
||||
onRemoveRelation,
|
||||
readActionAllowedFields,
|
||||
redirectToPreviousPage,
|
||||
@ -650,7 +742,11 @@ const EditViewDataManagerProvider = ({
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<OverlayBlocker key="overlayBlocker" isOpen={isSubmitting} {...overlayBlockerParams} />
|
||||
<OverlayBlocker
|
||||
key="overlayBlocker"
|
||||
isOpen={isSubmitting || isPublishing}
|
||||
{...overlayBlockerParams}
|
||||
/>
|
||||
{isLoading ? (
|
||||
<LoadingIndicatorPage />
|
||||
) : (
|
||||
|
||||
@ -210,9 +210,11 @@ const reducer = (state, action) => {
|
||||
.update('modifiedDZName', () => null)
|
||||
.update('formErrors', () => fromJS(action.errors));
|
||||
case 'SUBMIT_ERRORS':
|
||||
case 'PUBLISH_ERRORS':
|
||||
return state
|
||||
.update('formErrors', () => fromJS(action.errors))
|
||||
.update('shouldShowLoadingState', () => false);
|
||||
case 'PUBLISH_SUCCESS':
|
||||
case 'SUBMIT_SUCCESS':
|
||||
case 'DELETE_SUCCEEDED':
|
||||
return state
|
||||
|
||||
@ -151,5 +151,6 @@
|
||||
"popUpWarning.warning.cancelAllSettings": "Are you sure you want to cancel your modifications?",
|
||||
"popUpWarning.warning.updateAllSettings": "This will modify all your settings",
|
||||
"success.record.delete": "Deleted",
|
||||
"success.record.publish": "Published",
|
||||
"success.record.save": "Saved"
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ const generatePermissionsObject = uid => {
|
||||
const permissions = {
|
||||
create: [{ action: 'plugins::content-manager.explorer.create', subject: null }],
|
||||
delete: [{ action: 'plugins::content-manager.explorer.delete', subject: null }],
|
||||
publish: [{ action: 'plugins::content-manager.explorer.publish', subject: null }],
|
||||
read: [{ action: 'plugins::content-manager.explorer.read', subject: null }],
|
||||
update: [{ action: 'plugins::content-manager.explorer.update', subject: null }],
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user