Skip to content

Commit

Permalink
API Standardise endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Jul 17, 2024
1 parent 94193a5 commit 69c8c72
Show file tree
Hide file tree
Showing 23 changed files with 6,300 additions and 684 deletions.
4,946 changes: 4,924 additions & 22 deletions client/dist/js/bundle.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions client/dist/styles/bundle.css

Large diffs are not rendered by default.

88 changes: 55 additions & 33 deletions client/src/boot/registerTransforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import Injector from 'lib/Injector';
import readOneBlockQuery from 'state/history/readOneBlockQuery';
import HistoricElementViewFactory from 'components/HistoricElementView/HistoricElementView';
import revertToBlockVersionMutation from 'state/history/revertToBlockVersionMutation';
import revertToBlockVersionRequest from 'state/history/revertToBlockVersionRequest';
import readBlocksForAreaQuery from 'state/editor/readBlocksForAreaQuery';
import addElementToArea from 'state/editor/addElementMutation';
import ArchiveAction from 'components/ElementActions/ArchiveAction';
import DuplicateAction from 'components/ElementActions/DuplicateAction';
import SaveAction from 'components/ElementActions/SaveAction';
import PublishAction from 'components/ElementActions/PublishAction';
import UnpublishAction from 'components/ElementActions/UnpublishAction';
import { getConfig } from 'state/editor/elementConfig';

export default () => {
Injector.transform(
Expand Down Expand Up @@ -37,41 +39,61 @@ export default () => {
}
);

Injector.transform(
'blocks-history-revert',
(updater) => {
// Add block element revert GraphQL mutation to the HistoryViewerToolbar
updater.component(
'HistoryViewerToolbar.VersionedAdmin.HistoryViewer.Element.HistoryViewerVersionDetail',
revertToBlockVersionMutation,
'BlockRevertMutation'
);
}
);
// REST
if (!getConfig().useGraphql) {
Injector.transform(
'blocks-history-revert',
(updater) => {
// Add revertToVersion() to props.actions on HistoryViewerToolbar
updater.component(
'HistoryViewerToolbar.VersionedAdmin.HistoryViewer.Element.HistoryViewerVersionDetail',
revertToBlockVersionRequest,
'BlockRevertRequest'
);
}
);
}

Injector.transform(
'cms-element-editor',
(updater) => {
// Add GraphQL query for reading elements on a page for the ElementEditor
updater.component(
'ElementList',
readBlocksForAreaQuery,
'PageElements'
);
}
);
// GRAPHQL
if (getConfig().useGraphql) {
Injector.transform(
'blocks-history-revert',
(updater) => {
// Add block element revert GraphQL mutation to the HistoryViewerToolbar
// This is the yellow revert button at the bottom on
// /admin/pages/edit/EditForm/6/field/ElementalArea/item/2/edit#Root_History
updater.component(
'HistoryViewerToolbar.VersionedAdmin.HistoryViewer.Element.HistoryViewerVersionDetail',
revertToBlockVersionMutation,
'BlockRevertMutation'
);
}
);

Injector.transform(
'cms-element-adder',
(updater) => {
// Add GraphQL query for adding elements to an ElementEditor (ElementalArea)
updater.component(
'AddElementPopover',
addElementToArea,
'ElementAddButton'
);
}
);
Injector.transform(
'cms-element-editor',
(updater) => {
// Add GraphQL query for reading elements on a page for the ElementEditor
updater.component(
'ElementList',
readBlocksForAreaQuery,
'PageElements'
);
}
);

Injector.transform(
'cms-element-adder',
(updater) => {
// Add GraphQL query for adding elements to an ElementEditor (ElementalArea)
updater.component(
'AddElementPopover',
addElementToArea,
'ElementAddButton'
);
}
);
}

// Add elemental editor actions
Injector.transform('element-actions', (updater) => {
Expand Down
38 changes: 25 additions & 13 deletions client/src/components/ElementActions/ArchiveAction.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,51 @@
/* global window */
import React from 'react';
import React, { useContext } from 'react';
import { compose } from 'redux';
import AbstractAction from 'components/ElementActions/AbstractAction';
import archiveBlockMutation from 'state/editor/archiveBlockMutation';
import i18n from 'i18n';
import { ElementEditorContext } from 'components/ElementEditor/ElementEditor';
import backend from 'lib/Backend';
import { getConfig } from 'state/editor/elementConfig';

/**
* Adds the elemental menu action to archive a block of any state
*/
const ArchiveAction = (MenuComponent) => (props) => {
const { fetchBlocks } = useContext(ElementEditorContext);

const handleClick = (event) => {
event.stopPropagation();

const { element: { id }, isPublished, actions: { handleArchiveBlock } } = props;

const isPublished = props.element.isPublished;
let archiveMessage = i18n._t(
'ElementArchiveAction.CONFIRM_DELETE',
'Are you sure you want to send this block to the archive?'
);

if (isPublished) {
archiveMessage = i18n._t(
'ElementArchiveAction.CONFIRM_DELETE_AND_UNPUBLISH',
'Warning: This block will be unpublished before being sent to the archive. Are you sure you want to proceed?'
);
}

// eslint-disable-next-line no-alert
if (handleArchiveBlock && window.confirm(archiveMessage)) {
handleArchiveBlock(id).then(() => {
const preview = window.jQuery('.cms-preview');
if (preview && typeof preview.entwine === 'function') {
if (!window.confirm(archiveMessage)) {
return;
}
if (getConfig().useGraphql) {
const { element: { id }, actions: { handleArchiveBlock } } = props;
// eslint-disable-next-line no-alert
if (handleArchiveBlock) {
handleArchiveBlock(id).then(() => {
const preview = window.jQuery('.cms-preview');
preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src'));
}
});
});
}
} else {
const id = props.element.id;
const url = `${getConfig().controllerLink.replace(/\/$/, '')}/archive`;
backend.post(url, {
ID: id,
})
.then(() => fetchBlocks());
}
};

Expand Down
30 changes: 21 additions & 9 deletions client/src/components/ElementActions/DuplicateAction.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
/* global window */
import React from 'react';
import React, { useContext } from 'react';
import { compose } from 'redux';
import AbstractAction from 'components/ElementActions/AbstractAction';
import duplicateBlockMutation from 'state/editor/duplicateBlockMutation';
import i18n from 'i18n';
import { ElementEditorContext } from 'components/ElementEditor/ElementEditor';
import backend from 'lib/Backend';
import { getConfig } from 'state/editor/elementConfig';

/**
* Adds the elemental menu action to duplicate a block
*/
const DuplicateAction = (MenuComponent) => (props) => {
const { fetchBlocks } = useContext(ElementEditorContext);

if (props.type.broken) {
// Don't allow this action for a broken element.
return (
Expand All @@ -18,14 +23,21 @@ const DuplicateAction = (MenuComponent) => (props) => {

const handleClick = (event) => {
event.stopPropagation();

const { element: { id }, actions: { handleDuplicateBlock } } = props;

if (handleDuplicateBlock) {
handleDuplicateBlock(id).then(() => {
const preview = window.jQuery('.cms-preview');
preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src'));
});
if (getConfig().useGraphql) {
const { element: { id }, actions: { handleDuplicateBlock } } = props;
if (handleDuplicateBlock) {
handleDuplicateBlock(id).then(() => {
const preview = window.jQuery('.cms-preview');
preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src'));
});
}
} else {
const id = props.element.id;
const url = `${getConfig().controllerLink.replace(/\/$/, '')}/duplicate`;
backend.post(url, {
ID: id,
})
.then(() => fetchBlocks());
}
};

Expand Down
116 changes: 116 additions & 0 deletions client/src/components/ElementActions/PublishAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,72 @@ import React, { useContext } from 'react';
import AbstractAction from 'components/ElementActions/AbstractAction';
import i18n from 'i18n';
import { ElementContext } from 'components/ElementEditor/Element';
import backend from 'lib/Backend';
import { loadElementSchemaValue } from 'state/editor/loadElementSchemaValue';
import { ElementEditorContext } from 'components/ElementEditor/ElementEditor';
import { getConfig } from 'state/editor/elementConfig';

/**
* Show a toast message reporting whether publication of Element was successful
*
* @param {string} type E.g. "Content" - human friendly element type (not PHP FQCN)
* @param {string} title Title of the element, or a false value if unset (e.g. undefined)
* @param {boolean} success Show a success message (true), or an error message (false)
*/
const reportPublicationStatus = (type, title, success) => {
const noTitle = i18n.inject(
i18n._t('ElementHeader.NOTITLE', 'Untitled {type} block'),
{ type }
);
const successMessage = i18n.inject(
i18n._t('ElementPublishAction.SUCCESS_NOTIFICATION', 'Published \'{title}\' successfully'),
{ title: title || noTitle }
);
const errorMessage = i18n.inject(
i18n._t('ElementPublishAction.ERROR_NOTIFICATION', 'Error publishing \'{title}\''),
{ title: title || noTitle }
);
window.jQuery.noticeAdd({
text: success ? successMessage : errorMessage,
stay: false,
type: success ? 'success' : 'error',
});
};

/**
* Post updated Element data to save it
*
* @param {number} id Element ID
* @param {object} formData Information to be saved
* @param {string} securityId Security ID for form submission
*/
const performSaveForElementWithFormData = (id, formData, securityId) => {
const saveEndpoint = backend.createEndpointFetcher({
url: loadElementSchemaValue('saveUrl', id),
method: loadElementSchemaValue('saveMethod'),
payloadFormat: loadElementSchemaValue('payloadFormat'),
defaultData: {
SecurityID: securityId
},
});

// Perform save & get new version number to publish
return saveEndpoint(formData)
.then(() => window.ss.apolloClient.queryManager.reFetchObservableQueries())
.then((input) => {
const preview = window.jQuery('.cms-preview');
preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src'));
return input;
})
.then((newPageData) => {
const newElementData = newPageData[0] && newPageData[0]
.data
.readOneElementalArea
.elements
.find((elementData) => elementData.id === id);
return newElementData && newElementData.version;
});
};

/**
* Adds the elemental menu action to publish a draft/modified block
Expand All @@ -12,12 +78,62 @@ const PublishAction = (MenuComponent) => (props) => {
formDirty,
onPublishButtonClick,
} = useContext(ElementContext);
const { fetchBlocks } = useContext(ElementEditorContext);

if (props.type.broken) {
// Don't allow this action for a broken element.
return (
<MenuComponent {...props} />
);
}

const { element } = props;

const publishElement = () => {
if (getConfig().useGraphql) {
const { element: { id }, actions: { handlePublishBlock } } = props;
return handlePublishBlock(id);
} else {
const id = props.element.id;
const url = `${getConfig().controllerLink.replace(/\/$/, '')}/publish`;
return backend.post(url, {
ID: id,
})
.then(() => fetchBlocks());
}
};

const handleClick = (event) => {
event.stopPropagation();
onPublishButtonClick();

const {
element: {
id,
title,
},
type,
securityId,
formData,
reinitialiseForm,
} = props;

let actionFlow = new Promise((resolve) => resolve());

// Edits have been made to the form. Peform a "Save & Publish"
if (formDirty) {
actionFlow = performSaveForElementWithFormData(id, formData, securityId)
.then((passthrough) => {
reinitialiseForm(formData);
return passthrough;
});
}

// Perform publish. Data is assumed to be up to date
actionFlow
.then(() => publishElement())
.then(() => reportPublicationStatus(type.title, title, true))
.catch(() => reportPublicationStatus(type.title, title, false));
};

const disabled = props.element.canPublish !== undefined && !props.element.canPublish;
Expand Down
Loading

0 comments on commit 69c8c72

Please sign in to comment.