From f6177e55095268e3b06aa70b0600c2c313c49c6c Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Mon, 22 Jul 2024 17:41:27 +1200 Subject: [PATCH] API Standardise endpoints --- _config/graphql.yml | 10 - _graphql/config.yml | 5 - _graphql/models.yml | 41 --- _graphql/mutations.yml | 9 - _graphql/scalars.yml | 4 - client/dist/js/bundle.js | 110 +------ client/src/boot/registerTransforms.js | 47 +-- .../ElementActions/ArchiveAction.js | 32 +- .../ElementActions/DuplicateAction.js | 26 +- .../ElementActions/UnpublishAction.js | 84 +++-- .../tests/ArchiveAction-test.js | 140 +++++--- .../tests/DuplicateAction-test.js | 44 ++- .../tests/PublishAction-test.js | 99 +++--- .../ElementActions/tests/SaveAction-test.js | 97 +++--- .../tests/UnpublishAction-test.js | 100 ++++-- .../ElementEditor/AddElementPopover.js | 30 +- .../src/components/ElementEditor/Element.js | 44 ++- .../components/ElementEditor/ElementEditor.js | 158 +++++---- .../components/ElementEditor/ElementList.js | 42 +-- .../ElementEditor/tests/Element-test.js | 105 +++--- .../ElementEditor/tests/ElementEditor-test.js | 27 +- .../ElementEditor/tests/ElementList-test.js | 12 +- client/src/legacy/ElementEditor/entwine.js | 9 +- client/src/state/editor/addElementMutation.js | 42 --- .../src/state/editor/archiveBlockMutation.js | 36 --- .../state/editor/duplicateBlockMutation.js | 38 --- .../src/state/editor/publishBlockMutation.js | 9 - .../state/editor/readBlocksForAreaQuery.js | 65 ---- client/src/state/editor/sortBlockMutation.js | 92 ------ .../state/editor/unpublishBlockMutation.js | 45 --- client/src/state/history/readOneBlockQuery.js | 96 ------ .../history/revertToBlockVersionMutation.js | 43 --- .../history/revertToBlockVersionRequest.js | 22 ++ client/src/types/elementTypeType.js | 2 +- composer.json | 1 - package.json | 3 - src/Controllers/ElementalAreaController.php | 303 +++++++++++++++++- src/GraphQL/Resolvers/Resolver.php | 197 ------------ src/Models/BaseElement.php | 56 +--- src/ORM/FieldType/DBObjectType.php | 21 -- src/Services/ElementTypeRegistry.php | 5 +- .../GraphQL/AddElementToAreaMutationTest.php | 95 ------ .../GraphQL/AddElementToAreaMutationTest.yml | 2 - .../GraphQL/DuplicateElementMutationTest.php | 72 ----- tests/GraphQL/FakeResolveInfo.php | 28 -- .../GraphQL/SortBlockMutationCreatorTest.php | 64 ---- .../GraphQL/SortBlockMutationCreatorTest.yml | 13 - tests/Reports/ElementsInUseReportTest.php | 21 -- yarn.lock | 144 ++------- 49 files changed, 994 insertions(+), 1796 deletions(-) delete mode 100644 _config/graphql.yml delete mode 100644 _graphql/config.yml delete mode 100644 _graphql/models.yml delete mode 100644 _graphql/mutations.yml delete mode 100644 _graphql/scalars.yml delete mode 100644 client/src/state/editor/addElementMutation.js delete mode 100644 client/src/state/editor/archiveBlockMutation.js delete mode 100644 client/src/state/editor/duplicateBlockMutation.js delete mode 100644 client/src/state/editor/publishBlockMutation.js delete mode 100644 client/src/state/editor/readBlocksForAreaQuery.js delete mode 100644 client/src/state/editor/sortBlockMutation.js delete mode 100644 client/src/state/editor/unpublishBlockMutation.js delete mode 100644 client/src/state/history/readOneBlockQuery.js delete mode 100644 client/src/state/history/revertToBlockVersionMutation.js create mode 100644 client/src/state/history/revertToBlockVersionRequest.js delete mode 100644 src/GraphQL/Resolvers/Resolver.php delete mode 100644 src/ORM/FieldType/DBObjectType.php delete mode 100644 tests/GraphQL/AddElementToAreaMutationTest.php delete mode 100644 tests/GraphQL/AddElementToAreaMutationTest.yml delete mode 100644 tests/GraphQL/DuplicateElementMutationTest.php delete mode 100644 tests/GraphQL/FakeResolveInfo.php delete mode 100644 tests/GraphQL/SortBlockMutationCreatorTest.php delete mode 100644 tests/GraphQL/SortBlockMutationCreatorTest.yml diff --git a/_config/graphql.yml b/_config/graphql.yml deleted file mode 100644 index 13b2b076..00000000 --- a/_config/graphql.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -Name: elemental-graphql -Only: - classexists: 'SilverStripe\GraphQL\Schema\Schema' ---- -SilverStripe\GraphQL\Schema\Schema: - schemas: - admin: - src: - elemental: 'dnadesign/silverstripe-elemental: _graphql' diff --git a/_graphql/config.yml b/_graphql/config.yml deleted file mode 100644 index 08e7c7be..00000000 --- a/_graphql/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -resolvers: - - 'DNADesign\Elemental\GraphQL\Resolvers\Resolver' -typeMapping: - DNADesign\Elemental\Models\BaseElement: Block - DNADesign\Elemental\Models\ElementalArea: ElementalArea diff --git a/_graphql/models.yml b/_graphql/models.yml deleted file mode 100644 index 77e585aa..00000000 --- a/_graphql/models.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Expose common static fields for the ElementEditor component to use for preview summaries -DNADesign\Elemental\Models\BaseElement: - fields: - id: true - lastEdited: true - absoluteLink: String - title: true - showTitle: true - sort: true - blockSchema: ObjectType - obsoleteClassName: String - isPublished: Boolean - isLiveVersion: Boolean - canCreate: Boolean - canPublish: Boolean - canUnpublish: Boolean - canDelete: Boolean - operations: - copyToStage: true - readOne: true - delete: true - publish: true - unpublish: true -# Expose access to Elements via Page -> ElementalArea -> Elements (see resolver) -DNADesign\Elemental\Models\ElementalArea: - fields: - id: true - elements: - plugins: - paginateList: false - operations: - readOne: true - -# Basic member information is required for history views -SilverStripe\Security\Member: - fields: - id: true - firstName: true - surname: true - operations: - readOne: true diff --git a/_graphql/mutations.yml b/_graphql/mutations.yml deleted file mode 100644 index 48ecba4d..00000000 --- a/_graphql/mutations.yml +++ /dev/null @@ -1,9 +0,0 @@ -'addElementToArea(className: String!, elementalAreaID: ID!, afterElementID: ID)': - model: DNADesign\Elemental\Models\BaseElement - description: Adds an Element to an ElementalArea, optionally after another Element -'duplicateBlock(id: ID!)': - model: DNADesign\Elemental\Models\BaseElement - description: Duplicate an Element in this ElementalArea -'sortBlock(id: ID!, afterBlockID: ID!)': - model: DNADesign\Elemental\Models\BaseElement - description: Changes the sort position of an element diff --git a/_graphql/scalars.yml b/_graphql/scalars.yml deleted file mode 100644 index 1bffd517..00000000 --- a/_graphql/scalars.yml +++ /dev/null @@ -1,4 +0,0 @@ -ObjectType: - serialiser: [DNADesign\Elemental\GraphQL\Resolvers\Resolver, 'serialiseObjectType'] - literalParser: [DNADesign\Elemental\GraphQL\Resolvers\Resolver, 'parseLiteralObjectType'] - valueParser: [DNADesign\Elemental\GraphQL\Resolvers\Resolver, 'parseValueObjectType'] diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 9931ca54..aee6fca4 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1,109 +1 @@ -!function(){"use strict";var e={2038:function(e,t,n){var r=a(n(4121)),o=a(n(1240));function a(e){return e&&e.__esModule?e:{default:e}}window.document.addEventListener("DOMContentLoaded",(()=>{(0,r.default)(),(0,o.default)()}))},4121:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=v(n(5207)),o=v(n(584)),a=v(n(7105)),l=v(n(6721)),i=v(n(2512)),u=v(n(5563)),d=v(n(5297)),s=v(n(7651)),c=v(n(1305)),f=v(n(7660)),p=v(n(7333)),m=v(n(2382)),g=v(n(8953)),b=v(n(1950)),h=v(n(6573));function v(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{r.default.component.registerMany({ElementEditor:l.default,ElementToolbar:u.default,ElementAddNewButton:d.default,ElementList:i.default,Element:o.default,ElementActions:a.default,ElementHeader:s.default,ElementContent:c.default,ElementSummary:f.default,ElementInlineEditForm:p.default,AddElementPopover:m.default,HoverBar:g.default,DragPositionIndicator:b.default,TextCheckboxGroupField:h.default})}},1240:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=m(n(5207)),o=m(n(5071)),a=m(n(2977)),l=m(n(8633)),i=m(n(3521)),u=m(n(6283)),d=m(n(4)),s=m(n(9283)),c=m(n(8501)),f=m(n(2221)),p=m(n(5904));function m(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{r.default.transform("elemental-fieldgroup",(e=>{e.component("FieldGroup.HistoryViewer.VersionDetail",a.default,"HistoricElement")}),{after:"field-holders"}),r.default.transform("elements-history",(e=>{e.component("HistoryViewer.Form_ItemEditForm",o.default,"ElementHistoryViewer")})),r.default.transform("blocks-history-revert",(e=>{e.component("HistoryViewerToolbar.VersionedAdmin.HistoryViewer.Element.HistoryViewerVersionDetail",l.default,"BlockRevertMutation")})),r.default.transform("cms-element-editor",(e=>{e.component("ElementList",i.default,"PageElements")})),r.default.transform("cms-element-adder",(e=>{e.component("AddElementPopover",u.default,"ElementAddButton")})),r.default.transform("element-actions",(e=>{e.component("ElementActions",c.default,"ElementActionsWithSave"),e.component("ElementActions",f.default,"ElementActionsWithPublish"),e.component("ElementActions",p.default,"ElementActionsWithUnpublish"),e.component("ElementActions",s.default,"ElementActionsWithDuplicate"),e.component("ElementActions",d.default,"ElementActionsWithArchive")}))}},26:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(1594)),o=u(n(6935)),a=u(n(6923)),l=n(3556),i=n(9791);function u(e){return e&&e.__esModule?e:{default:e}}const d=e=>{const{className:t,title:n,label:o}=e,i={className:(0,a.default)(t,"dropdown-item"),...e};return r.default.createElement(l.DropdownItem,i,o||n)};d.propTypes={disabled:o.default.bool,className:o.default.string,onClick:o.default.func,title:o.default.string,name:o.default.string,type:i.elementTypeType,active:o.default.bool,label:o.default.string},d.defaultProps={disabled:!1};t.default=d},4:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=u(n(1594)),o=n(9367),a=u(n(26)),l=u(n(1429)),i=u(n(5815));function u(e){return e&&e.__esModule?e:{default:e}}const d=e=>t=>{const n=void 0!==t.element.canDelete&&!t.element.canDelete,o=i.default._t("ElementArchiveAction.ARCHIVE","Archive"),l={label:o,title:n?i.default._t("ElementArchiveAction.ARCHIVE_PERMISSION_DENY","Archive, insufficient permissions"):o,disabled:n,className:"element-editor__actions-archive",onClick:e=>{e.stopPropagation();const{element:{id:n},isPublished:r,actions:{handleArchiveBlock:o}}=t;let a=i.default._t("ElementArchiveAction.CONFIRM_DELETE","Are you sure you want to send this block to the archive?");r&&(a=i.default._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?")),o&&window.confirm(a)&&o(n).then((()=>{const e=window.jQuery(".cms-preview");e&&"function"==typeof e.entwine&&e.entwine("ss.preview")._loadUrl(e.find("iframe").attr("src"))}))},toggle:t.toggle};return r.default.createElement(e,t,t.children,r.default.createElement(a.default,l))};t.Component=d;t.default=(0,o.compose)(l.default,d)},9283:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=u(n(1594)),o=n(9367),a=u(n(26)),l=u(n(9324)),i=u(n(5815));function u(e){return e&&e.__esModule?e:{default:e}}const d=e=>t=>{if(t.type.broken)return r.default.createElement(e,t);const n=void 0!==t.element.canCreate&&!t.element.canCreate,o=i.default._t("ElementArchiveAction.DUPLICATE","Duplicate"),l={label:o,title:n?i.default._t("ElementArchiveAction.DUPLICATE_PERMISSION_DENY","Duplicate, insufficient permissions"):o,disabled:n,className:"element-editor__actions-duplicate",onClick:e=>{e.stopPropagation();const{element:{id:n},actions:{handleDuplicateBlock:r}}=t;r&&r(n).then((()=>{const e=window.jQuery(".cms-preview");e.entwine("ss.preview")._loadUrl(e.find("iframe").attr("src"))}))},toggle:t.toggle};return r.default.createElement(e,t,t.children,r.default.createElement(a.default,l))};t.Component=d;t.default=(0,o.compose)(l.default,d)},2221:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=i(n(26)),a=i(n(5815)),l=n(584);function i(e){return e&&e.__esModule?e:{default:e}}function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}const d=e=>t=>{const{formDirty:n,onPublishButtonClick:i}=(0,r.useContext)(l.ElementContext),{element:u}=t,d=void 0!==t.element.canPublish&&!t.element.canPublish,s=a.default._t("ElementArchiveAction.PUBLISH","Publish"),c={label:s,title:d?a.default._t("ElementArchiveAction.PUBLISH_PERMISSION_DENY","Publish, insufficient permissions"):s,disabled:d,className:"element-editor__actions-publish",onClick:e=>{e.stopPropagation(),i()},toggle:t.toggle};return t.type.broken?r.default.createElement(e,t):r.default.createElement(e,t,t.children,(n||!u.isLiveVersion)&&r.default.createElement(o.default,c))};t.Component=d;t.default=d},8501:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=i(n(26)),a=i(n(5815)),l=n(584);function i(e){return e&&e.__esModule?e:{default:e}}function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}const d=e=>t=>{const{onSaveButtonClick:n,formDirty:i}=(0,r.useContext)(l.ElementContext),u={title:a.default._t("ElementSaveAction.SAVE","Save"),className:"element-editor__actions-save",onClick:e=>{e.stopPropagation(),n()},toggle:t.toggle};return!t.expandable||t.type.broken?r.default.createElement(e,t):r.default.createElement(e,t,t.children,i&&r.default.createElement(o.default,u))};t.Component=d;t.default=d},5904:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=u(n(1594)),o=n(9367),a=u(n(26)),l=u(n(2393)),i=u(n(5815));function u(e){return e&&e.__esModule?e:{default:e}}const d=e=>t=>{if(t.type.broken)return r.default.createElement(e,t);const{element:n,type:o,actions:{handleUnpublishBlock:l}}=t,u=void 0!==t.element.canUnpublish&&!t.element.canUnpublish,d=i.default._t("ElementArchiveAction.UNPUBLISH","Unpublish"),s={label:d,title:u?i.default._t("ElementArchiveAction.UNPUBLISH_PERMISSION_DENY","Unpublish, insufficient permissions"):d,disabled:u,className:"element-editor__actions-unpublish",onClick:e=>{e.stopPropagation();const{jQuery:t}=window,r=i.default.inject(i.default._t("ElementHeader.NOTITLE","Untitled {type} block"),{type:o.title});l&&l(n.id).then((()=>{const e=t(".cms-preview");e.entwine("ss.preview")._loadUrl(e.find("iframe").attr("src")),t.noticeAdd({text:i.default.inject(i.default._t("ElementUnpublishAction.SUCCESS_NOTIFICATION","Removed '{title}' from the published page"),{title:n.title||r}),stay:!1,type:"success"})})).catch((()=>{t.noticeAdd({text:i.default.inject(i.default._t("ElementUnpublishAction.ERROR_NOTIFICATION","Error unpublishing '{title}'"),{title:n.title||r}),stay:!1,type:"error"})}))},toggle:t.toggle};return r.default.createElement(e,t,t.children,n.isPublished&&r.default.createElement(a.default,s))};t.Component=d;t.default=(0,o.compose)(l.default,d)},2382:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=s(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=d(n(6935)),a=d(n(6923)),l=n(5207),i=n(9791),u=d(n(5815));function d(e){return e&&e.__esModule?e:{default:e}}function s(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(s=function(e){return e?n:t})(e)}class c extends r.Component{constructor(e){super(e),this.handleToggle=this.handleToggle.bind(this)}getElementButtonClickHandler(e){return t=>{const{actions:{handleAddElementToArea:n},insertAfterElement:r}=this.props;t.preventDefault(),n(e.class,r).then((()=>{const e=window.jQuery(".cms-preview");e.entwine("ss.preview")._loadUrl(e.find("iframe").attr("src"))})),this.handleToggle()}}handleToggle(){const{toggle:e}=this.props;e()}render(){const{PopoverOptionSetComponent:e,elementTypes:t,container:n,extraClass:o,isOpen:l,placement:i,target:d}=this.props,s=(0,a.default)("element-editor-add-element",o),c=t.map((e=>({content:e.title,key:e.name,className:(0,a.default)(e.icon,"btn--icon-xl","element-editor-add-element__button"),onClick:this.getElementButtonClickHandler(e)})));return r.default.createElement(e,{buttons:c,searchPlaceholder:u.default._t("ElementAddElementPopover.SEARCH_BLOCKS","Search blocks"),extraClass:s,container:n,isOpen:l,placement:i,target:d,toggle:this.handleToggle})}}c.propTypes={container:o.default.oneOfType([o.default.string,o.default.func,o.default.object]),elementTypes:o.default.arrayOf(i.elementTypeType).isRequired,extraClass:o.default.oneOfType([o.default.string,o.default.array,o.default.object]),isOpen:o.default.bool.isRequired,placement:o.default.string,target:o.default.oneOfType([o.default.string,o.default.func,o.default.object]).isRequired,toggle:o.default.func.isRequired,areaId:o.default.number.isRequired,insertAfterElement:o.default.oneOfType([o.default.number,o.default.string])};t.default=(0,l.inject)(["PopoverOptionSet"],(e=>({PopoverOptionSetComponent:e})),(()=>"ElementEditor"))(c)},5297:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=s(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=d(n(6935)),a=n(3556),l=d(n(5815)),i=n(9791),u=n(5207);function d(e){return e&&e.__esModule?e:{default:e}}function s(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(s=function(e){return e?n:t})(e)}class c extends r.Component{constructor(e){super(e),this.toggle=this.toggle.bind(this),this.state={popoverOpen:!1}}toggle(){this.setState((e=>({popoverOpen:!e.popoverOpen})))}render(){const{AddElementPopoverComponent:e,elementTypes:t,areaId:n}=this.props,o={id:`ElementalArea${n}_AddButton`,color:"primary",onClick:this.toggle,className:"font-icon-plus"};return r.default.createElement("div",null,r.default.createElement(a.Button,o,l.default._t("ElementAddNewButton.ADD_BLOCK","Add block")),r.default.createElement(e,{placement:"bottom-start",target:o.id,isOpen:this.state.popoverOpen,elementTypes:t,toggle:this.toggle,areaId:n,insertAfterElement:0}))}}t.Component=c,c.defaultProps={},c.propTypes={elementTypes:o.default.arrayOf(i.elementTypeType).isRequired,areaId:o.default.number.isRequired};t.default=(0,u.inject)(["AddElementPopover"],(e=>({AddElementPopoverComponent:e})),(()=>"ElementEditor.ElementList.AddNewButton"))(c)},1305:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r,o=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),a=(r=n(6935))&&r.__esModule?r:{default:r},l=n(5207),i=n(9367);function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}class d extends o.PureComponent{render(){const{id:e,fileUrl:t,fileTitle:n,content:r,previewExpanded:a,InlineEditFormComponent:l,SummaryComponent:i,activeTab:u,onFormInit:d,handleLoadingError:s,formDirty:c,broken:f,onFormSchemaSubmitResponse:p,ensureFormRendered:m,formHasRendered:g}=this.props,b=!a&&(m||g),h={"element-editor-editform--collapsed":!a,"element-editor-editform--rendered-not-visible":b};return o.default.createElement("div",{className:"element-editor-content"},!a&&o.default.createElement(i,{content:r,fileUrl:t,fileTitle:n,broken:f}),(a||m||g)&&o.default.createElement(l,{extraClass:h,onClick:e=>e.stopPropagation(),elementId:e,activeTab:u,onFormInit:d,handleLoadingError:s,onFormSchemaSubmitResponse:p,notVisible:b}),c&&o.default.createElement("input",{type:"hidden",name:"change-tracker",className:"element-form-dirty-state",value:"1"}))}}t.Component=d,d.propTypes={id:a.default.string,content:a.default.string,fileUrl:a.default.string,fileTitle:a.default.string,previewExpanded:a.default.bool,SummaryComponent:a.default.elementType,InlineEditFormComponent:a.default.elementType,handleLoadingError:a.default.func,broken:a.default.bool,onFormSchemaSubmitResponse:a.default.func,onFormInit:a.default.func,ensureFormRendered:a.default.bool,formHasRendered:a.default.bool,formDirty:a.default.object},d.defaultProps={};t.default=(0,i.compose)((0,l.inject)(["ElementSummary","ElementInlineEditForm"],((e,t)=>({SummaryComponent:e,InlineEditFormComponent:t})),(()=>"ElementEditor.ElementList.Element")))(d)},1950:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=o(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&{}.hasOwnProperty.call(e,l)){var i=a?Object.getOwnPropertyDescriptor(e,l):null;i&&(i.get||i.set)?Object.defineProperty(r,l,i):r[l]=e[l]}return r.default=e,n&&n.set(e,r),r}(n(1594));function o(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(o=function(e){return e?n:t})(e)}class a extends r.PureComponent{render(){return r.default.createElement("div",{className:"elemental-editor-drag-indicator"},r.default.createElement("div",{className:"elemental-editor-drag-indicator__ball"}))}}t.default=a},584:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.ElementContext=t.Component=void 0;var r=P(n(1594)),o=n(8695),a=k(n(6935)),l=n(455),i=n(9791),u=n(9367),d=n(5207),s=k(n(5815)),c=k(n(6923)),f=n(9040),p=n(5381),m=n(2153),g=n(7785),b=n(4242),h=n(3521),v=P(n(5034)),y=n(1820),_=n(8724),E=n(9306),O=P(n(8918)),T=n(9077);function k(e){return e&&e.__esModule?e:{default:e}}function D(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(D=function(e){return e?n:t})(e)}function P(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=D(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}const I=t.ElementContext=(0,r.createContext)(null),w=e=>{const[t,n]=(0,r.useState)(!1),[a,l]=(0,r.useState)(""),[i,u]=(0,r.useState)(!1),[d,f]=(0,r.useState)(e.element.title),[p,m]=(0,r.useState)(!1),[g,v]=(0,r.useState)(!1),[y,E]=(0,r.useState)(!1),[O,T]=(0,r.useState)(!1),[k,D]=(0,r.useState)(!1),[P,w]=(0,r.useState)(!1),[C,S]=(0,r.useState)(!1),[j]=(0,o.useMutation)(b.publishBlockMutation);(0,r.useEffect)((()=>{e.connectDragPreview&&e.connectDragPreview((0,_.getEmptyImage)(),{captureDraggingState:!0}),e.formStateExists&&w(!0)}),[]),(0,r.useEffect)((()=>{p&&P&&(m(!1),e.formDirty?(e.submitForm(),T(!0)):E(!0))}),[p,P]),(0,r.useEffect)((()=>{C&&(S(!1),e.dispatchAddFormChanged())}),[C]);const M=()=>s.default.inject(s.default._t("ElementHeader.NOTITLE","Untitled {type} block"),{type:e.type.title}),A=()=>window.ss.apolloClient.queryManager.refetchQueries({include:[{query:h.query,variables:{id:e.areaId}}]}),N=t=>{(t=>{const n=d||M();if(t){const t=s.default.inject(s.default._t("ElementPublishAction.ERROR_NOTIFICATION","Error publishing '{title}'"),{title:n});e.actions.toasts.error(t)}else{const t=s.default.inject(s.default._t("ElementPublishAction.SUCCESS_NOTIFICATION","Published '{title}' successfully"),{title:n});e.actions.toasts.success(t)}})(t),E(!1),T(!1),Promise.all(A()).then((()=>{setTimeout((()=>e.dispatchRemoveFormChanged()),250)}))};(0,r.useEffect)((()=>{P&&g&&(e.submitForm(),v(!1))}),[P,g]),(0,r.useEffect)((()=>{P&&y&&j({variables:{blockId:e.element.id}}).then((()=>N(!1))).catch((()=>N(!0)))}),[P,y]);const R=t=>{const{tabSetName:n,onActivateTab:r}=e;if(a||l(t),t||a)r(n,t||a);else{r(n,"Main")}},x=r=>{const{type:o,link:a}=e;o.broken||("button"!==r.target.type?!o.inlineEditable||i?window.location=a:n(!t):r.stopPropagation())},{element:B,type:q,areaId:L,HeaderComponent:F,ContentComponent:$,link:W,activeTab:H,connectDragSource:V,connectDropTarget:U,isDragging:Q,isOver:K,onDragEnd:G,formDirty:Y}=e;if(!B.id)return null;const z=(0,c.default)("element-editor__element",{"element-editor__element--broken":q.broken,"element-editor__element--expandable":q.inlineEditable&&!q.broken,"element-editor__element--dragging":Q,"element-editor__element--dragged-over":K},(()=>{const{element:t}=e,n="element-editor__element";return t.isPublished?t.isPublished&&!t.isLiveVersion?`${n}--modified`:`${n}--published`:`${n}--draft`})()),J={formDirty:Y,onPublishButtonClick:()=>{m(!0),D(!0)},onSaveButtonClick:()=>{D(!0),v(!0)}},X=U(r.default.createElement("div",{className:z,onClick:x,onKeyUp:e=>{const{nodeName:t}=e.target;" "!==e.key&&"Enter"!==e.key||["input","textarea"].includes(t.toLowerCase())||x(e)},role:"button",tabIndex:0,title:(e=>e.broken?s.default._t("ElementalElement.ARCHIVE_BROKEN","Archive this block"):s.default.inject(s.default._t("ElementalElement.TITLE","Edit this {type} block"),{type:e.title}))(q),key:B.id},r.default.createElement(I.Provider,{value:J},r.default.createElement(F,{element:B,type:q,areaId:L,expandable:q.inlineEditable,link:W,previewExpanded:t,handleEditTabsClick:t=>{const{activeTab:r}=e;t===r||i||(n(!0),R(t))},activeTab:H,disableTooltip:Q,onDragEnd:G}),r.default.createElement($,{id:B.id,fileUrl:B.blockSchema.fileURL,fileTitle:B.blockSchema.fileTitle,content:((e,t)=>t.broken?e.title?s.default.inject(s.default._t("ElementalElement.BROKEN_DESCRIPTION_TITLE",'This block had the title "{title}". It is broken and will not display on the front-end. You can archive it to remove it from this elemental area.'),{title:e.title}):s.default._t("ElementalElement.BROKEN_DESCRIPTION","This block is broken and will not display on the front-end. You can archive it to remove it from this elemental area."):e.blockSchema.content)(B,q),previewExpanded:t&&!Q,ensureFormRendered:k,formHasRendered:P,activeTab:H,handleLoadingError:()=>{u(!0)},broken:q.broken,onFormSchemaSubmitResponse:(t,r)=>{if(t.id.match(/\/schema\/elemental-area\/([0-9]+)/))return e.type.inlineEditable&&n(!0),S(!0),void(O&&T(!1));f(r),O&&(T(!1),E(!0)),y||O||(t=>{const n=t||M(),r=s.default.inject(s.default._t("ElementSaveAction.SUCCESS_NOTIFICATION","Saved '{title}' successfully"),{title:n});e.actions.toasts.success(r)})(r),A()},onFormInit:()=>(e=>{R(e),w(!0)})(H)}))));return t?X:V(X)};t.Component=w,w.propTypes={element:l.elementType,type:i.elementTypeType.isRequired,areaId:a.default.number.isRequired,link:a.default.string.isRequired,activeTab:a.default.string,tabSetName:a.default.string,onActivateTab:a.default.func,connectDragSource:a.default.func.isRequired,connectDragPreview:a.default.func.isRequired,connectDropTarget:a.default.func.isRequired,isDragging:a.default.bool.isRequired,isOver:a.default.bool.isRequired,onDragOver:a.default.func,onDragEnd:a.default.func,onDragStart:a.default.func},w.defaultProps={element:null};const C={drop(e,t,n){const{element:r}=e;return{target:r.id,dropSpot:(0,E.isOverTop)(t,n)?"top":"bottom"}},hover(e,t,n){const{element:r,onDragOver:o}=e;o&&o(r,(0,E.isOverTop)(t,n))}};t.default=(0,u.compose)((0,y.DropTarget)("element",C,((e,t)=>({connectDropTarget:e.dropTarget(),isOver:t.isOver()}))),(0,y.DragSource)("element",E.elementDragSource,((e,t)=>({connectDragSource:e.dragSource(),connectDragPreview:e.dragPreview(),isDragging:t.isDragging()}))),(0,f.connect)((function(e,t){const n=t.element.id,r=(0,m.loadElementFormStateName)(n),o=(0,g.loadElementSchemaValue)("schemaUrl",n),a=e.form&&e.form.formSchemas[o]&&e.form.formSchemas[o].schema&&e.form.formSchemas[o].schema.fields.find((e=>"Tabs"===e.component)),l=a&&a.id,i=`element.${r}__${l}`,u=e.unsavedForms.find((e=>e.name===`element.${r}`)),d=e.form&&e.form.formState&&e.form.formState.element&&e.form.formState.element.hasOwnProperty(r);return{tabSetName:l,activeTab:e.tabs&&e.tabs.fields&&e.tabs.fields[i]&&e.tabs.fields[i].activeTab,formDirty:u,formStateExists:d}}),(function(e,t){const n=(0,m.loadElementFormStateName)(t.element.id);return{onActivateTab(t,r){e(v.activateTab(`element.${n}__${t}`,r))},submitForm(){e((0,p.submit)(`element.${n}`))},dispatchAddFormChanged(){e((0,T.addFormChanged)(`element.${n}`))},dispatchRemoveFormChanged(){e((0,T.removeFormChanged)(`element.${n}`))},actions:{toasts:(0,u.bindActionCreators)(O,e)}}})),(0,d.inject)(["ElementHeader","ElementContent"],((e,t)=>({HeaderComponent:e,ContentComponent:t})),(()=>"ElementEditor.ElementList.Element")))(w)},7105:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=f(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=c(n(6935)),a=n(9367),l=n(3556),i=n(5207),u=n(455),d=n(9791),s=c(n(26));function c(e){return e&&e.__esModule?e:{default:e}}function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(f=function(e){return e?n:t})(e)}class p extends r.Component{constructor(e){super(e),this.handleEditTabsClick=this.handleEditTabsClick.bind(this)}handleEditTabsClick(e){const{handleEditTabsClick:t}=this.props;t(e.target.name)}renderEditTabs(){const{editTabs:e,activeTab:t,type:n,expandable:o}=this.props;return!n.broken&&o&&e&&e.length?e.map((e=>{let{name:o,title:a}=e;return r.default.createElement(s.default,{key:o,name:o,title:a,type:n,onClick:this.handleEditTabsClick,active:o===t})})):null}renderDivider(){const{children:e,editTabs:t,expandable:n}=this.props;return n&&t&&t.length&&0!==r.default.Children.count(e)?r.default.createElement(l.DropdownItem,{divider:!0,role:"separator"}):null}render(){const{children:e,id:t,ActionMenuComponent:n}=this.props;return r.default.createElement(n,{id:`element-editor-actions-${t}`,className:"element-editor-header__actions-dropdown",dropdownMenuProps:{right:!0},dropdownToggleClassNames:["element-editor-header__actions-toggle","btn","btn-sm","btn--no-text","font-icon-dot-3"]},this.renderEditTabs(),this.renderDivider(),e)}}t.Component=p,p.propTypes={element:u.elementType,type:d.elementTypeType.isRequired,areaId:o.default.number.isRequired,activeTab:o.default.string,editTabs:o.default.arrayOf(o.default.shape({title:o.default.string,name:o.default.string})),handleEditTabsClick:o.default.func.isRequired,expandable:o.default.bool},p.defaultProps={editTabs:[],expandable:!0};t.default=(0,a.compose)((0,i.inject)(["ActionMenu"],(e=>({ActionMenuComponent:e})),(()=>"ElementEditor.ElementList.Element")))(p)},920:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=c(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=s(n(6935)),a=s(n(7651)),l=n(1820),i=n(455),u=n(9791),d=n(1135);function s(e){return e&&e.__esModule?e:{default:e}}function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(c=function(e){return e?n:t})(e)}class f extends r.Component{render(){const{isDragging:e,element:t,elementTypes:n,currentOffset:o}=this.props;if(!e||!o)return null;const{x:l,y:i}=o,u=`translate(${l}px, ${i}px)`,s={transform:u,WebkitTransform:u},c=(0,d.getElementTypeConfig)(t,n);return r.default.createElement("div",{className:"element-editor-drag-preview",style:s},r.default.createElement(a.default,{element:t,type:c,simple:!0}))}}f.propTypes={element:i.elementType,elementTypes:o.default.arrayOf(u.elementTypeType),isDragging:o.default.bool,currentOffset:o.default.shape({x:o.default.number.isRequired,y:o.default.number.isRequired})};t.default=(0,l.DragLayer)((e=>({element:e.getItem(),currentOffset:e.getSourceClientOffset(),isDragging:e.isDragging()})))(f)},6721:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=b(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=g(n(6935)),a=n(5207),l=n(9367),i=n(9791),u=n(9040),d=n(2153),s=n(1820),c=g(n(3695)),f=g(n(920)),p=g(n(5799)),m=n(2325);function g(e){return e&&e.__esModule?e:{default:e}}function b(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(b=function(e){return e?n:t})(e)}class h extends r.PureComponent{constructor(e){super(e),this.state={dragTargetElementId:null,dragSpot:null},this.handleDragOver=this.handleDragOver.bind(this),this.handleDragEnd=this.handleDragEnd.bind(this)}handleDragOver(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=!!e&&e.id;this.setState({dragTargetElementId:n,dragSpot:!1===t?"bottom":"top"})}handleDragEnd(e,t){const{actions:{handleSortBlock:n},areaId:r}=this.props;n(e,t,r).then((()=>{const e=window.jQuery(".cms-preview");e.entwine("ss.preview")._loadUrl(e.find("iframe").attr("src"))})),this.setState({dragTargetElementId:null,dragSpot:null})}render(){const{fieldName:e,formState:t,ToolbarComponent:n,ListComponent:o,areaId:a,elementTypes:l,isDraggingOver:i,connectDropTarget:u,allowedElements:d}=this.props,{dragTargetElementId:s,dragSpot:c}=this.state,p=d.map((e=>l.find((t=>t.class===e))));return u(r.default.createElement("div",{className:"element-editor"},r.default.createElement(n,{elementTypes:p,areaId:a,onDragOver:this.handleDragOver}),r.default.createElement(o,{allowedElementTypes:p,elementTypes:l,areaId:a,onDragOver:this.handleDragOver,onDragStart:this.handleDragStart,onDragEnd:this.handleDragEnd,dragSpot:c,isDraggingOver:i,dragTargetElementId:s}),r.default.createElement(f.default,{elementTypes:l}),r.default.createElement("input",{name:e,type:"hidden",value:JSON.stringify(t)||"",className:"no-change-track"})))}}t.Component=h,h.propTypes={fieldName:o.default.string,elementTypes:o.default.arrayOf(i.elementTypeType).isRequired,allowedElements:o.default.arrayOf(o.default.string).isRequired,areaId:o.default.number.isRequired,actions:o.default.shape({handleSortBlock:o.default.func})};const v={},y=(0,m.createSelector)([e=>{const t=e.form.formState.element;return t||v}],(e=>{const t=(0,d.loadElementFormStateName)("[0-9]+");return Object.keys(e).filter((e=>e.match(t))).reduce(((t,n)=>({...t,[n]:e[n].values})),{})}));t.default=(0,l.compose)(p.default,(0,s.DropTarget)("element",{},((e,t)=>({connectDropTarget:e.dropTarget(),isDraggingOver:t.isOver()}))),(0,u.connect)((function(e){return{formState:y(e)}})),(0,a.inject)(["ElementToolbar","ElementList"],((e,t)=>({ToolbarComponent:e,ListComponent:t})),(()=>"ElementEditor")),c.default)(h)},2512:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=g(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=m(n(6935)),a=n(455),l=n(9791),i=n(9367),u=n(5207),d=m(n(6923)),s=m(n(5815)),c=n(1820),f=n(9306),p=n(1135);function m(e){return e&&e.__esModule?e:{default:e}}function g(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(g=function(e){return e?n:t})(e)}class b extends r.Component{getDragIndicatorIndex(){const{dragTargetElementId:e,draggedItem:t,blocks:n,dragSpot:r}=this.props;return(0,f.getDragIndicatorIndex)(n.map((e=>e.id)),e,t&&t.id,r)}renderBlocks(){const{ElementComponent:e,HoverBarComponent:t,DragIndicatorComponent:n,blocks:o,allowedElementTypes:a,elementTypes:l,areaId:i,onDragEnd:u,onDragOver:d,onDragStart:c,isDraggingOver:f}=this.props;if(!o)return null;if(o&&!o.length)return r.default.createElement("div",null,s.default._t("ElementList.ADD_BLOCKS","Add blocks to place your content"));let m=o.map((n=>r.default.createElement("div",{key:n.id},r.default.createElement(e,{element:n,areaId:i,type:(0,p.getElementTypeConfig)(n,l),link:n.blockSchema.actions.edit,onDragOver:d,onDragEnd:u,onDragStart:c}),f||r.default.createElement(t,{key:`create-after-${n.id}`,areaId:i,elementId:n.id,elementTypes:a}))));f||(m=[r.default.createElement(t,{key:0,areaId:i,elementId:0,elementTypes:a})].concat(m));const g=this.getDragIndicatorIndex();return f&&null!==g&&m.splice(g,0,r.default.createElement(n,{key:"DropIndicator"})),m}renderLoading(){const{loading:e,LoadingComponent:t}=this.props;return e?r.default.createElement(t,null):null}render(){const{blocks:e}=this.props,t=(0,d.default)("elemental-editor-list",{"elemental-editor-list--empty":!e||!e.length});return this.props.connectDropTarget(r.default.createElement("div",{className:t},this.renderLoading(),this.renderBlocks()))}}t.Component=b,b.propTypes={blocks:o.default.arrayOf(a.elementType),elementTypes:o.default.arrayOf(l.elementTypeType).isRequired,allowedElementTypes:o.default.arrayOf(l.elementTypeType).isRequired,loading:o.default.bool,areaId:o.default.number.isRequired,dragTargetElementId:o.default.oneOfType([o.default.string,o.default.bool]),onDragOver:o.default.func,onDragStart:o.default.func,onDragEnd:o.default.func},b.defaultProps={blocks:[],loading:!1};const h={drop(e,t){const{blocks:n}=e,r=t.getDropResult();if(!r)return{};const o=(0,f.getDragIndicatorIndex)(n.map((e=>e.id)),r.target,t.getItem(),r.dropSpot),a=n[o-1]?n[o-1].id:"0";return{...r,dropAfterID:a}}};t.default=(0,i.compose)((0,c.DropTarget)("element",h,((e,t)=>({connectDropTarget:e.dropTarget(),draggedItem:t.getItem()}))),(0,u.inject)(["Element","Loading","HoverBar","DragPositionIndicator"],((e,t,n,r)=>({ElementComponent:e,LoadingComponent:t,HoverBarComponent:n,DragIndicatorComponent:r})),(()=>"ElementEditor.ElementList")))(b)},7651:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=b(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=g(n(6935)),a=n(3556),l=n(455),i=n(9791),u=n(9367),d=n(5207),s=g(n(5815)),c=g(n(6923)),f=n(1820),p=n(9306),m=n(8724);function g(e){return e&&e.__esModule?e:{default:e}}function b(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(b=function(e){return e?n:t})(e)}class h extends r.Component{constructor(e){super(e),this.toggle=this.toggle.bind(this),this.state={tooltipOpen:!1}}componentDidMount(){const{connectDragPreview:e}=this.props;e&&e((0,m.getEmptyImage)(),{captureDraggingState:!0})}componentDidUpdate(){const{tooltipOpen:e}=this.state,{disableTooltip:t}=this.props;e&&t&&this.setState({tooltipOpen:!1})}getBlockTitle(e,t){return t.broken?s.default.inject(s.default._t("ElementHeader.BROKEN","This element is of obsolete type {type}."),{type:t.obsoleteClassName}):e.title?e.title:s.default.inject(s.default._t("ElementHeader.NOTITLE","Untitled {type} block"),{type:t.title})}toggle(){this.setState((e=>({tooltipOpen:!e.tooltipOpen})))}renderVersionedStateMessage(){const{element:{isLiveVersion:e,isPublished:t}}=this.props;if(t&&e)return null;let n="";const o=["element-editor-header__version-state"];return t?e||(n=s.default._t("ElementHeader.STATE_MODIFIED","Item has unpublished changes"),o.push("element-editor-header__version-state--modified")):(n=s.default._t("ElementHeader.STATE_DRAFT","Item has not been published yet"),o.push("element-editor-header__version-state--draft")),r.default.createElement("span",{className:(0,c.default)(o),title:n})}renderStatusBadge(){const{element:{isLiveVersion:e,isPublished:t}}=this.props;if(t&&e)return null;let n="",o="";const a=["badge"];return t?e||(n=s.default._t("ElementHeader.BADGE_MODIFIED","Modified"),o=s.default._t("ElementHeader.STATE_MODIFIED","Item has unpublished changes"),a.push("status-modified")):(n=s.default._t("ElementHeader.BADGE_DRAFT","Draft"),o=s.default._t("ElementHeader.STATE_DRAFT","Item has not been published yet"),a.push("status-addedtodraft")),r.default.createElement("span",{className:(0,c.default)(a),title:o},n)}render(){const{connectDragSource:e,element:t,type:n,areaId:o,previewExpanded:l,simple:i,disableTooltip:u,activeTab:d,expandable:f,ElementActionsComponent:p,handleEditTabsClick:m}=this.props,g=this.getBlockTitle(t,n),b=(0,c.default)({"element-editor-header__title":!0,"element-editor-header__title--none":!t.title}),h=s.default._t("ElementHeader.EXPAND","Show editable fields"),v=(0,c.default)("element-editor-header",{"element-editor-header--simple":i}),y=(0,c.default)("element-editor-header__icon-container",{"element-editor-header__icon-container--broken":n.broken}),_=(0,c.default)("element-editor-header__expand",{"font-icon-right-open-big":!f,"font-icon-up-open-big":f&&l,"font-icon-down-open-big":f&&!l}),E=`element-icon-${t.id}`,O=r.default.createElement("div",{className:v},r.default.createElement("div",{className:"element-editor-header__drag-handle"},r.default.createElement("i",{className:"font-icon-drag-handle"})),r.default.createElement("div",{className:"element-editor-header__info"},r.default.createElement("div",{className:y},r.default.createElement("i",{className:n.icon,id:E}),this.renderVersionedStateMessage(),!n.broken&&!i&&r.default.createElement(a.Tooltip,{placement:"top",isOpen:this.state.tooltipOpen&&!u,target:E,toggle:this.toggle},n.title)),r.default.createElement("h3",{className:b},g),this.renderStatusBadge()),!i&&r.default.createElement("div",{className:"element-editor-header__actions"},r.default.createElement("div",{role:"none",onClick:e=>e.stopPropagation()},r.default.createElement(p,{element:t,type:n,areaId:o,activeTab:d,editTabs:n.editTabs,handleEditTabsClick:m,expandable:f})),!n.broken&&r.default.createElement("i",{className:_,title:h})));return l?e(O):O}}t.Component=h,h.propTypes={element:l.elementType.isRequired,type:i.elementTypeType.isRequired,areaId:o.default.number,activeTab:o.default.string,simple:o.default.bool,ElementActionsComponent:o.default.elementType,previewExpanded:o.default.bool,disableTooltip:o.default.bool,connectDragSource:o.default.func.isRequired,connectDragPreview:o.default.func.isRequired,onDragEnd:o.default.func},h.defaultProps={expandable:!0};t.default=(0,u.compose)((0,f.DragSource)("element",p.elementDragSource,(e=>({connectDragSource:e.dragSource(),connectDragPreview:e.dragPreview()}))),(0,d.inject)(["ElementActions"],(e=>({ElementActionsComponent:e})),(()=>"ElementEditor.ElementList.Element")))(h)},8953:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=d(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=u(n(6935)),a=n(5207),l=n(9791),i=u(n(5815));function u(e){return e&&e.__esModule?e:{default:e}}function d(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(d=function(e){return e?n:t})(e)}const s=(0,u(n(7577)).default)("element-editor__hover-bar");function c(e){let{AddElementPopoverComponent:t,elementTypes:n,elementId:o,areaId:a,popoverOpen:l,onToggle:u}=e;const d=`${s("-line")} font-icon-plus-circled`,c=i.default._t("ElementAddNewButton.ADD_BLOCK","Add block"),f={className:s("-area",{"-area--focus":l}),onClick:u,"aria-label":c,title:c,id:`AddBlockHoverBarArea_${a}_${o}`};return r.default.createElement("div",{className:s(""),id:`AddBlockHoverBar_${a}_${o}`},r.default.createElement("button",f,r.default.createElement("span",{className:s("-area-inner")},r.default.createElement("span",{className:d}))),r.default.createElement(t,{placement:"bottom",target:`AddBlockHoverBarArea_${a}_${o}`,isOpen:l,elementTypes:n,toggle:u,container:`#AddBlockHoverBar_${a}_${o}`,areaId:a,insertAfterElement:o}))}class f extends r.Component{constructor(e){super(e),this.toggle=this.toggle.bind(this),this.state={popoverOpen:!1}}toggle(){this.setState((e=>({popoverOpen:!e.popoverOpen})))}render(){const e={...this.state,...this.props,onToggle:this.toggle};return r.default.createElement(c,e)}}t.Component=f,f.propTypes={elementTypes:o.default.arrayOf(l.elementTypeType).isRequired,elementId:o.default.oneOfType([o.default.string,o.default.number]).isRequired,areaId:o.default.oneOfType([o.default.number,o.default.string]).isRequired};t.default=(0,a.inject)(["AddElementPopover"],(e=>({AddElementPopoverComponent:e})),(()=>"ElementEditor.ElementList.HoverBar"))(f)},7333:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=f(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=c(n(6935)),a=c(n(6923)),l=c(n(55)),i=n(7785),u=c(n(5815)),d=n(2153),s=n(9040);function c(e){return e&&e.__esModule?e:{default:e}}function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(f=function(e){return e?n:t})(e)}function p(){return p=Object.assign?Object.assign.bind():function(e){for(var t=1;t{t.match(/PageElements_[0-9]+_Title/)&&(r=e[t])})),n().then((e=>this.props.onFormSchemaSubmitResponse(e,r)))}render(){const{elementId:e,extraClass:t,onClick:n,onFormInit:o,formHasState:u,notVisible:d}=this.props,{loadingError:s}=this.state,c=(0,a.default)("element-editor-editform",t),f={formTag:"form",schemaUrl:(0,i.loadElementSchemaValue)("schemaUrl",e),identifier:"element",refetchSchemaOnMount:!u,onLoadingError:this.handleLoadingError,onSubmit:this.handleSubmit};s&&(f.loading=!1),"function"==typeof o&&(f.onReduxFormInit=o);const m={};return d&&(m["aria-hidden"]="true"),r.default.createElement("div",p({className:c,onClick:n,role:"presentation"},m),r.default.createElement(l.default,f))}}m.propTypes={extraClass:o.default.oneOfType([o.default.string,o.default.object]),onClick:o.default.func,elementId:o.default.string,handleLoadingError:o.default.func,onFormSchemaSubmitResponse:o.default.func,notVisible:o.default.bool};t.default=(0,s.connect)((function(e,t){const n=(0,d.loadElementFormStateName)(t.elementId);return{formHasState:e.form.formState&&e.form.formState.element&&!!e.form.formState.element[n]}}))(m)},7660:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=i(n(6935)),a=i(n(6923)),l=i(n(5815));function i(e){return e&&e.__esModule?e:{default:e}}function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}class d extends r.PureComponent{render(){const{fileUrl:e,fileTitle:t,content:n,broken:o}=this.props,i=l.default._t("ElementSummary.NO_PREVIEW","No preview available"),u=(0,a.default)("element-editor-summary__content",{"element-editor-summary__content--broken":o});return r.default.createElement("div",{className:"element-editor-summary"},e&&r.default.createElement("img",{className:"element-editor-summary__thumbnail-image",src:e,alt:t}),(n||!e)&&r.default.createElement("p",{className:u},n||i))}}d.defaultProps={},d.propTypes={content:o.default.string,fileUrl:o.default.string,fileTitle:o.default.string,broken:o.default.bool};t.default=d},5563:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=d(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),a=(r=n(6935))&&r.__esModule?r:{default:r},l=n(5207),i=n(9791),u=n(1820);function d(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(d=function(e){return e?n:t})(e)}class s extends o.PureComponent{render(){const{AddNewButtonComponent:e,elementTypes:t,areaId:n,connectDropTarget:r}=this.props;return r(o.default.createElement("div",{className:"element-editor__toolbar"},o.default.createElement(e,{elementTypes:t,areaId:n})))}}s.defaultProps={},s.propTypes={elementTypes:a.default.arrayOf(i.elementTypeType).isRequired,areaId:a.default.number.isRequired,AddNewButtonComponent:a.default.elementType.isRequired,connectDropTarget:a.default.func.isRequired,onDragOver:a.default.func,onDragDrop:a.default.func};const c={hover(e){const{onDragOver:t}=e;t&&t()}};t.default=(0,u.DropTarget)("element",c,(e=>({connectDropTarget:e.dropTarget()})))((0,l.inject)(["ElementAddNewButton"],(e=>({AddNewButtonComponent:e})),(()=>"ElementEditor.ElementToolbar"))(s))},2977:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=l(n(1594)),o=l(n(5815)),a=l(n(6923));function l(e){return e&&e.__esModule?e:{default:e}}t.default=e=>class extends e{getClassName(){const e=[super.getClassName()];return this.props.data.ElementID&&e.unshift("elemental-area__element--historic-inner"),(0,a.default)(e)}render(){const e=this.getLegend(),t=this.props.data.tag||"div",n=this.getClassName(),{data:a}=this.props;return a.ElementID?r.default.createElement(t,{className:n},e,r.default.createElement("div",{className:"elemental-preview elemental-preview--historic"},a.ElementEditLink&&r.default.createElement("a",{className:"elemental-preview__link",href:a.ElementEditLink},r.default.createElement("span",{className:"elemental-preview__link-text"},o.default._t("HistoricElementView.VIEW_BLOCK_HISTORY","Block history")),r.default.createElement("i",{className:"font-icon-angle-right btn--icon-lg elemental-preview__link-caret"})),r.default.createElement("div",{className:"elemental-preview__icon"},r.default.createElement("i",{className:a.ElementIcon})),r.default.createElement("div",{className:"elemental-preview__detail"},r.default.createElement("h3",null,a.ElementTitle," ",r.default.createElement("small",null,a.ElementType)))),this.props.children):super.render()}}},6573:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=l(n(1594)),o=n(3556),a=l(n(2623));function l(e){return e&&e.__esModule?e:{default:e}}t.default=(0,a.default)((e=>{const{children:t}=e,n=r.default.Children.toArray(r.default.Children.map(t,((t,n)=>{const o={};return 0===n?(o.id=e.id,o.title=null):1===n&&(o.noHolder=!0),r.default.cloneElement(t,o)})));return e.readOnly?r.default.createElement("div",{className:"text-checkbox-group-field--read-only"},n):1===n.length?n[0]:r.default.createElement(o.InputGroup,{className:"text-checkbox-group-field"},n[0],r.default.createElement(o.InputGroupAddon,{addonType:"append"},r.default.createElement(o.InputGroupText,null,n[1])))}))},51:function(e,t,n){var r=d(n(1669)),o=d(n(1594)),a=n(5145),l=n(5207),i=n(1135),u=n(5381);function d(e){return e&&e.__esModule?e:{default:e}}const s=()=>{window.ss.apolloClient.resetStore(),setTimeout((()=>{const{store:e}=window.ss;e&&e.dispatch((0,u.destroy)(...Object.keys(e.getState().form.formState.element||{}).map((e=>`element.${e}`))))}),0)};r.default.entwine("ss",(e=>{e(".js-injector-boot .element-editor__container").entwine({ReactRoot:null,onmatch(){const e=(0,l.loadComponent)("ElementEditor",{}),t=this.data("schema"),n=(0,i.getConfig)().elementTypes,r={fieldName:this.attr("name"),areaId:t["elemental-area-id"],allowedElements:t["allowed-elements"],elementTypes:n};let u=this.getReactRoot();u||(u=(0,a.createRoot)(this[0]),this.setReactRoot(u)),u.render(o.default.createElement(e,r))},onunmatch(){e(".cms-edit-form").data("hasValidationErrors")||s();const t=this.getReactRoot();t&&(t.unmount(),this.setReactRoot(null))},"from .cms-edit-form":{onaftersubmitform(t,n){const r=JSON.parse(n.xhr.responseText).ValidationResult;JSON.parse(r.replace(/<\/?script[^>]*?>/g,"")).isValid?(e(".cms-edit-form").data("hasValidationErrors",!1),s()):e(".cms-edit-form").data("hasValidationErrors",!0)}}}),e(".js-injector-boot .element-editor__container .element-form-dirty-state").entwine({onmatch(){e(".cms-edit-form").trigger("change")},onunmatch(){e(".cms-edit-form").trigger("change")}}),e(".cms-edit-form").entwine({getChangeTrackerOptions(){const t=void 0===this.entwineData("ChangeTrackerOptions");let n=this._super();return t&&(n=e.extend({},n),n.ignoreFieldSelector+=", .elementalarea :input:not(.element-form-dirty-state)",this.setChangeTrackerOptions(n)),n}})}))},9306:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.isOverTop=t.getDragIndicatorIndex=t.elementDragSource=void 0;var r=n(4518);t.isOverTop=(e,t)=>{const n=e.getClientOffset(),o=(0,r.findDOMNode)(t).getBoundingClientRect();return n.y{if(null===t||!n)return null;let o=t?e.findIndex((e=>e===t)):0;const a=e.findIndex((e=>e===n));return"bottom"===r&&(o+=1),a===o||a+1===o?null:o};t.elementDragSource={beginDrag(e){return e.element},endDrag(e,t){const{onDragEnd:n}=e,r=t.getDropResult();if(!n||!r||!r.dropAfterID)return;const o=t.getItem().id,{dropAfterID:a}=r;o!==a&&n(o,a)}}},7577:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=(r=n(6923))&&r.__esModule?r:{default:r};t.default=e=>function(){const t=t=>`${e}${t}`;for(var n=arguments.length,r=new Array(n),a=0;a!(!e&&""!==e)&&("object"==typeof e?Array.isArray(e)?e.map(t):Object.entries(e).reduce(((e,n)=>{let[r,o]=n;return Object.assign({},e,{[t(r)]:o})}),{}):t(e))));return(0,o.default)(...l)}},6283:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.mutation=t.default=t.config=void 0;var r,o=n(708),a=(r=n(7284))&&r.__esModule?r:{default:r},l=n(3521);const i=t.mutation=a.default` -mutation AddElementToArea($className: String!, $elementalAreaID: ID!, $afterElementID: ID) { - addElementToArea( - className: $className, - elementalAreaID: $elementalAreaID, - afterElementID: $afterElementID - ) { - id - } -} -`,u=t.config={props:e=>{let{mutate:t,ownProps:{actions:n,areaId:r}}=e;return{actions:{...n,handleAddElementToArea:(e,n)=>t({variables:{className:e,elementalAreaID:r,afterElementID:n}})}}},options:e=>{let{areaId:t}=e;return{refetchQueries:[{query:l.query,variables:l.config.options({areaId:t}).variables}]}}};t.default=(0,o.graphql)(i,u)},1429:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.mutation=t.default=t.config=void 0;var r,o=n(708),a=(r=n(7284))&&r.__esModule?r:{default:r},l=n(3521);const i=t.mutation=a.default` -mutation ArchiveBlock($blockId: ID!) { - deleteBlocks(ids: [$blockId]) -} -`,u=t.config={props:e=>{let{mutate:t,ownProps:{actions:n}}=e;return{actions:{...n,handleArchiveBlock:e=>t({variables:{blockId:e}})}}},options:e=>{let{areaId:t}=e;return{refetchQueries:[{query:l.query,variables:l.config.options({areaId:t}).variables}]}}};t.default=(0,o.graphql)(i,u)},9324:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.mutation=t.default=t.config=void 0;var r,o=n(708),a=(r=n(7284))&&r.__esModule?r:{default:r},l=n(3521);const i=t.mutation=a.default` -mutation DuplicateBlock($blockId: ID!) { - duplicateBlock(id: $blockId) { - id - } -} -`,u=t.config={props:e=>{let{mutate:t,ownProps:{actions:n}}=e;return{actions:{...n,handleDuplicateBlock:e=>t({variables:{blockId:e}})}}},options:e=>{let{areaId:t}=e;return{refetchQueries:[{query:l.query,variables:l.config.options({areaId:t}).variables}]}}};t.default=(0,o.graphql)(i,u)},1135:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.getElementTypeConfig=t.getConfig=void 0;var r,o=(r=n(6177))&&r.__esModule?r:{default:r};const a=()=>o.default.getSection("DNADesign\\Elemental\\Controllers\\ElementalAreaController");t.getConfig=a;t.getElementTypeConfig=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=e.blockSchema.typeName;let r=(Array.isArray(t)?t:a().elementTypes).find((e=>e.class===n||e.name===n));return e.obsoleteClassName&&(r=Object.assign({obsoleteClassName:e.obsoleteClassName},r),Object.preventExtensions(r)),r}},2153:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.loadElementFormStateName=void 0;var r,o=(r=n(6177))&&r.__esModule?r:{default:r};t.loadElementFormStateName=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;const t=o.default.getSection("DNADesign\\Elemental\\Controllers\\ElementalAreaController").form.elementForm.formNameTemplate;return e?t.replace("{id}",e):t}},7785:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.loadElementSchemaValue=void 0;var r,o=(r=n(6177))&&r.__esModule?r:{default:r};t.loadElementSchemaValue=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=o.default.getSection("DNADesign\\Elemental\\Controllers\\ElementalAreaController").form.elementForm[e]||"";return t?`${n}/${t}`:n}},4242:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.publishBlockMutation=void 0;var r,o=(r=n(7284))&&r.__esModule?r:{default:r};t.publishBlockMutation=o.default` -mutation PublishBlock($blockId:ID!) { - publishBlock(id: $blockId) { - id - } -} -`},3521:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.query=t.default=t.config=void 0;var r,o=n(708),a=(r=n(7284))&&r.__esModule?r:{default:r};const l=t.query=a.default` -query ReadBlocksForArea($id:ID!) { - readOneElementalArea(filter: { id: { eq: $id } }, versioning: { - mode: DRAFT - }){ - elements { - id - title - blockSchema - obsoleteClassName - isLiveVersion - isPublished - version - canCreate - canPublish - canUnpublish - canDelete - } - } -} -`,i=t.config={options(e){let{areaId:t}=e;return{variables:{id:t}}},props(e){let{data:{error:t,readOneElementalArea:n,loading:r}}=e,o=null;n&&(o=n.elements);return{loading:r||!o,blocks:o,graphQLErrors:t&&t.graphQLErrors&&t.graphQLErrors.map((e=>e.message))}}};t.default=(0,o.graphql)(l,i)},3695:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.mutation=t.default=t.config=void 0;var r,o=n(708),a=(r=n(7284))&&r.__esModule?r:{default:r},l=n(3521);const i=t.mutation=a.default` -mutation SortBlockMutation($blockId:ID!, $afterBlockId:ID!) { - sortBlock( - id: $blockId - afterBlockID: $afterBlockId - ) { - id - isLiveVersion - isPublished - } -} -`,u=t.config={props:e=>{let{mutate:t,ownProps:{actions:n}}=e;return{actions:{...n,handleSortBlock:(e,n,r)=>t({variables:{blockId:e,afterBlockId:n},optimisticResponse:{sortBlock:{id:e,isLiveVersion:!1,isPublished:!1,__typename:"Block"}},update:(t,o)=>{let{data:{sortBlock:a}}=o;const i=l.config.options({areaId:r}).variables,u=t.readQuery({query:l.query,variables:i}),d=JSON.parse(JSON.stringify(u));let s=d.readOneElementalArea.elements;const c=s.findIndex((t=>t.id===e)),f=s[c];if(Object.entries(a).forEach((e=>{let[t,n]=e;"__typename"!==t&&(f[t]=n)})),s.splice(c,1),"0"===n)s.unshift(f);else{let e=s.findIndex((e=>e.id===n));-1===e&&(e=c-1);const t=s.slice(e+1);s=s.slice(0,e+1),s.push(f),s=s.concat(t)}d.readOneElementalArea.elements=s,t.writeQuery({query:l.query,data:d,variables:i})}})}}}};t.default=(0,o.graphql)(i,u)},2393:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.mutation=t.default=t.config=void 0;var r,o=n(708),a=(r=n(7284))&&r.__esModule?r:{default:r},l=n(3521);const i=t.mutation=a.default` -mutation UnpublishBlock($blockId:ID!) { - unpublishBlock( - id: $blockId - ) { - id - } -} -`,u=t.config={props:e=>{let{mutate:t,ownProps:{actions:n}}=e;return{actions:{...n,handleUnpublishBlock:(e,n,r,o)=>t({variables:{blockId:e,fromStage:n,toStage:r,fromVersion:o}})}}},options:e=>{let{areaId:t}=e;return{refetchQueries:[{query:l.query,variables:l.config.options({areaId:t}).variables}]}}};t.default=(0,o.graphql)(i,u)},5071:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.query=t.default=t.config=void 0;var r,o=n(708),a=(r=n(7284))&&r.__esModule?r:{default:r};const l=t.query=a.default` -query ReadHistoryViewerBlock ($block_id: ID!, $limit: Int!, $offset: Int!) { - readOneBlock( - versioning: { - mode: LATEST - }, - filter: { id: { eq: $block_id } } - ) { - id - versions (limit: $limit, offset: $offset, sort: { version: DESC }) { - pageInfo { - totalCount - } - nodes { - version - absoluteLink - author { - firstName - surname - } - publisher { - firstName - surname - } - published - liveVersion - latestDraftVersion - lastEdited - } - } - } -} -`,i=t.config={options(e){let{recordId:t,limit:n,page:r}=e;return{variables:{limit:n,offset:((r||1)-1)*n,block_id:t}}},props(e){let{data:{error:t,refetch:n,readOneBlock:r,loading:o},ownProps:{actions:a={versions:{}},limit:l,recordId:i}}=e;const u=r||null;return{loading:o||!u,versions:u,graphQLErrors:t&&t.graphQLErrors&&t.graphQLErrors.map((e=>e.message)),actions:{...a,versions:{...u,goToPage(e){n({offset:((e||1)-1)*l,limit:l,block_id:i})}}}}}};t.default=(0,o.graphql)(l,i)},8633:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.mutation=t.default=t.config=void 0;var r,o=n(708),a=(r=n(7284))&&r.__esModule?r:{default:r};const l=t.mutation=a.default` -mutation revertBlockToVersion($id:ID!, $fromStage:VersionedStage!, $toStage:VersionedStage!, $fromVersion:Int!) { - copyBlockToStage(input: { - id: $id - fromVersion: $fromVersion - fromStage: $fromStage - toStage: $toStage - }) { - id - } -} -`,i=t.config={props:e=>{let{mutate:t,ownProps:{actions:n}}=e;return{actions:{...n,revertToVersion:(e,n,r,o)=>t({variables:{id:e,fromVersion:n,fromStage:r,toStage:o}})}}},options:{refetchQueries:["ReadHistoryViewerBlock"]}};t.default=(0,o.graphql)(l,i)},455:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.elementType=void 0;var r,o=(r=n(6935))&&r.__esModule?r:{default:r};t.elementType=o.default.shape({id:o.default.string.isRequired,title:o.default.string,blockSchema:o.default.object,inlineEditable:o.default.bool,published:o.default.bool,liveVersion:o.default.bool,version:o.default.number})},9791:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.elementTypeType=void 0;var r,o=(r=n(6935))&&r.__esModule?r:{default:r};t.elementTypeType=o.default.shape({name:o.default.string,title:o.default.string,icon:o.default.string,inlineEditable:o.default.bool,editTabs:o.default.arrayOf(o.default.shape({title:o.default.string,name:o.default.string})),config:o.default.object})},2325:function(e,t,n){n.r(t),n.d(t,{createSelector:function(){return i},createSelectorCreator:function(){return l},createStructuredSelector:function(){return u},defaultEqualityCheck:function(){return o},defaultMemoize:function(){return a}});var r="NOT_FOUND";var o=function(e,t){return e===t};function a(e,t){var n,a,l="object"==typeof t?t:{equalityCheck:t},i=l.equalityCheck,u=void 0===i?o:i,d=l.maxSize,s=void 0===d?1:d,c=l.resultEqualityCheck,f=function(e){return function(t,n){if(null===t||null===n||t.length!==n.length)return!1;for(var r=t.length,o=0;o-1){var a=n[o];return o>0&&(n.splice(o,1),n.unshift(a)),a.value}return r}return{get:o,put:function(t,a){o(t)===r&&(n.unshift({key:t,value:a}),n.length>e&&n.pop())},getEntries:function(){return n},clear:function(){n=[]}}}(s,f);function m(){var t=p.get(arguments);if(t===r){if(t=e.apply(null,arguments),c){var n=p.getEntries().find((function(e){return c(e.value,t)}));n&&(t=n.value)}p.put(arguments,t)}return t}return m.clearCache=function(){return p.clear()},m}function l(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r{(0,r.default)(),(0,o.default)()}))},4121:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=E(n(5207)),o=E(n(584)),a=E(n(7105)),l=E(n(6721)),i=E(n(2512)),d=E(n(5563)),u=E(n(5297)),s=E(n(7651)),c=E(n(1305)),f=E(n(7660)),p=E(n(7333)),m=E(n(2382)),g=E(n(8953)),h=E(n(1950)),b=E(n(6573));function E(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{r.default.component.registerMany({ElementEditor:l.default,ElementToolbar:d.default,ElementAddNewButton:u.default,ElementList:i.default,Element:o.default,ElementActions:a.default,ElementHeader:s.default,ElementContent:c.default,ElementSummary:f.default,ElementInlineEditForm:p.default,AddElementPopover:m.default,HoverBar:g.default,DragPositionIndicator:h.default,TextCheckboxGroupField:b.default})}},1240:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=c(n(5207)),o=c(n(2977)),a=c(n(3337)),l=c(n(4)),i=c(n(9283)),d=c(n(8501)),u=c(n(2221)),s=c(n(5904));function c(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{r.default.transform("elemental-fieldgroup",(e=>{e.component("FieldGroup.HistoryViewer.VersionDetail",o.default,"HistoricElement")}),{after:"field-holders"}),r.default.transform("blocks-history-revert",(e=>{e.component("HistoryViewerToolbar.VersionedAdmin.HistoryViewer.Element.HistoryViewerVersionDetail",a.default,"BlockRevertRequest")})),r.default.transform("element-actions",(e=>{e.component("ElementActions",d.default,"ElementActionsWithSave"),e.component("ElementActions",u.default,"ElementActionsWithPublish"),e.component("ElementActions",s.default,"ElementActionsWithUnpublish"),e.component("ElementActions",i.default,"ElementActionsWithDuplicate"),e.component("ElementActions",l.default,"ElementActionsWithArchive")}))}},26:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=d(n(1594)),o=d(n(6935)),a=d(n(6923)),l=n(3556),i=n(9791);function d(e){return e&&e.__esModule?e:{default:e}}const u=e=>{const{className:t,title:n,label:o}=e,i={className:(0,a.default)(t,"dropdown-item"),...e};return r.default.createElement(l.DropdownItem,i,o||n)};u.propTypes={disabled:o.default.bool,className:o.default.string,onClick:o.default.func,title:o.default.string,name:o.default.string,type:i.elementTypeType,active:o.default.bool,label:o.default.string},u.defaultProps={disabled:!1};t.default=u},4:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=s(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=u(n(26)),a=u(n(5815)),l=n(6721),i=u(n(5609)),d=n(1135);function u(e){return e&&e.__esModule?e:{default:e}}function s(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(s=function(e){return e?n:t})(e)}const c=e=>t=>{const{fetchElements:n}=(0,r.useContext)(l.ElementEditorContext),u=void 0!==t.element.canDelete&&!t.element.canDelete,s=a.default._t("ElementArchiveAction.ARCHIVE","Archive"),c={label:s,title:u?a.default._t("ElementArchiveAction.ARCHIVE_PERMISSION_DENY","Archive, insufficient permissions"):s,disabled:u,className:"element-editor__actions-archive",onClick:e=>{e.stopPropagation();const r=t.element.isPublished;let o=a.default._t("ElementArchiveAction.CONFIRM_DELETE","Are you sure you want to send this block to the archive?");if(r&&(o=a.default._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?")),!window.confirm(o))return;const l=t.element.id,u=`${(0,d.getConfig)().controllerLink.replace(/\/$/,"")}/api/archive`;i.default.post(u,{ID:l}).then((()=>n()))},toggle:t.toggle};return r.default.createElement(e,t,t.children,r.default.createElement(o.default,c))};t.Component=c;t.default=c},9283:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=s(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=u(n(26)),a=u(n(5815)),l=n(6721),i=u(n(5609)),d=n(1135);function u(e){return e&&e.__esModule?e:{default:e}}function s(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(s=function(e){return e?n:t})(e)}const c=e=>t=>{const{fetchElements:n}=(0,r.useContext)(l.ElementEditorContext);if(t.type.broken)return r.default.createElement(e,t);const u=void 0!==t.element.canCreate&&!t.element.canCreate,s=a.default._t("ElementArchiveAction.DUPLICATE","Duplicate"),c={label:s,title:u?a.default._t("ElementArchiveAction.DUPLICATE_PERMISSION_DENY","Duplicate, insufficient permissions"):s,disabled:u,className:"element-editor__actions-duplicate",onClick:e=>{e.stopPropagation();const r=t.element.id,o=`${(0,d.getConfig)().controllerLink.replace(/\/$/,"")}/api/duplicate`;i.default.post(o,{ID:r}).then((()=>n()))},toggle:t.toggle};return r.default.createElement(e,t,t.children,r.default.createElement(o.default,c))};t.Component=c;t.default=c},2221:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=d(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=i(n(26)),a=i(n(5815)),l=n(584);function i(e){return e&&e.__esModule?e:{default:e}}function d(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(d=function(e){return e?n:t})(e)}const u=e=>t=>{const{formDirty:n,onPublishButtonClick:i}=(0,r.useContext)(l.ElementContext),{element:d}=t,u=void 0!==t.element.canPublish&&!t.element.canPublish,s=a.default._t("ElementArchiveAction.PUBLISH","Publish"),c={label:s,title:u?a.default._t("ElementArchiveAction.PUBLISH_PERMISSION_DENY","Publish, insufficient permissions"):s,disabled:u,className:"element-editor__actions-publish",onClick:e=>{e.stopPropagation(),i()},toggle:t.toggle};return t.type.broken?r.default.createElement(e,t):r.default.createElement(e,t,t.children,(n||!d.isLiveVersion)&&r.default.createElement(o.default,c))};t.Component=u;t.default=u},8501:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=d(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=i(n(26)),a=i(n(5815)),l=n(584);function i(e){return e&&e.__esModule?e:{default:e}}function d(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(d=function(e){return e?n:t})(e)}const u=e=>t=>{const{onSaveButtonClick:n,formDirty:i}=(0,r.useContext)(l.ElementContext),d={title:a.default._t("ElementSaveAction.SAVE","Save"),className:"element-editor__actions-save",onClick:e=>{e.stopPropagation(),n()},toggle:t.toggle};return!t.expandable||t.type.broken?r.default.createElement(e,t):r.default.createElement(e,t,t.children,i&&r.default.createElement(o.default,d))};t.Component=u;t.default=u},5904:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=s(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=u(n(26)),a=u(n(5815)),l=u(n(5609)),i=n(6721),d=n(1135);function u(e){return e&&e.__esModule?e:{default:e}}function s(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(s=function(e){return e?n:t})(e)}const c=e=>t=>{const{fetchElements:n}=(0,r.useContext)(i.ElementEditorContext);if(t.type.broken)return r.default.createElement(e,t);const u=(e,t,n)=>{const r=a.default.inject(a.default._t("ElementHeader.NOTITLE","Untitled {type} block"),{type:e}),o=a.default.inject(a.default._t("ElementUnpublishAction.SUCCESS_NOTIFICATION","Removed '{title}' from the published page"),{title:t||r}),l=a.default.inject(a.default._t("ElementUnpublishAction.ERROR_NOTIFICATION","Error unpublishing '{title}'"),{title:t||r});window.jQuery.noticeAdd({text:n?o:l,stay:!1,type:n?"success":"error"})},{element:s,type:c}=t,f=void 0!==t.element.canUnpublish&&!t.element.canUnpublish,p=a.default._t("ElementArchiveAction.UNPUBLISH","Unpublish"),m={label:p,title:f?a.default._t("ElementArchiveAction.UNPUBLISH_PERMISSION_DENY","Unpublish, insufficient permissions"):p,disabled:f,className:"element-editor__actions-unpublish",onClick:e=>{e.stopPropagation(),(()=>{const e=t.element.id,r=`${(0,d.getConfig)().controllerLink.replace(/\/$/,"")}/api/unpublish`;return l.default.post(r,{ID:e}).then((()=>n()))})().then((()=>u(c.title,s.title,!0))).catch((()=>u(c.title,s.title,!1)))},toggle:t.toggle};return r.default.createElement(e,t,t.children,s.isPublished&&r.default.createElement(o.default,m))};t.Component=c;t.default=c},2382:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=f(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=c(n(6935)),a=c(n(6923)),l=n(5207),i=n(9791),d=c(n(5815)),u=c(n(5609)),s=n(6721);function c(e){return e&&e.__esModule?e:{default:e}}function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(f=function(e){return e?n:t})(e)}class p extends r.Component{constructor(e){super(e),this.handleToggle=this.handleToggle.bind(this),p.contextType=s.ElementEditorContext}getElementButtonClickHandler(e){return t=>{t.preventDefault(),u.default.post("/admin/elemental-area/api/add/",{elementClass:e.class,elementalAreaID:this.props.areaId,insertAfterElementID:this.props.insertAfterElement}).then((()=>{const{fetchElements:e}=this.context;return e()})).then((()=>{const e=window.jQuery(".cms-preview");e.entwine("ss.preview")._loadUrl(e.find("iframe").attr("src"))})),this.handleToggle()}}handleToggle(){const{toggle:e}=this.props;e()}render(){const{PopoverOptionSetComponent:e,elementTypes:t,container:n,extraClass:o,isOpen:l,placement:i,target:u}=this.props,s=(0,a.default)("element-editor-add-element",o),c=t.map((e=>({content:e.title,key:e.name,className:(0,a.default)(e.icon,"btn--icon-xl","element-editor-add-element__button"),onClick:this.getElementButtonClickHandler(e)})));return r.default.createElement(e,{buttons:c,searchPlaceholder:d.default._t("ElementAddElementPopover.SEARCH_BLOCKS","Search blocks"),extraClass:s,container:n,isOpen:l,placement:i,target:u,toggle:this.handleToggle})}}p.propTypes={container:o.default.oneOfType([o.default.string,o.default.func,o.default.object]),elementTypes:o.default.arrayOf(i.elementTypeType).isRequired,extraClass:o.default.oneOfType([o.default.string,o.default.array,o.default.object]),isOpen:o.default.bool.isRequired,placement:o.default.string,target:o.default.oneOfType([o.default.string,o.default.func,o.default.object]).isRequired,toggle:o.default.func.isRequired,areaId:o.default.number.isRequired,insertAfterElement:o.default.oneOfType([o.default.number,o.default.string])};t.default=(0,l.inject)(["PopoverOptionSet"],(e=>({PopoverOptionSetComponent:e})),(()=>"ElementEditor"))(p)},5297:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=s(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=u(n(6935)),a=n(3556),l=u(n(5815)),i=n(9791),d=n(5207);function u(e){return e&&e.__esModule?e:{default:e}}function s(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(s=function(e){return e?n:t})(e)}class c extends r.Component{constructor(e){super(e),this.toggle=this.toggle.bind(this),this.state={popoverOpen:!1}}toggle(){this.setState((e=>({popoverOpen:!e.popoverOpen})))}render(){const{AddElementPopoverComponent:e,elementTypes:t,areaId:n}=this.props,o={id:`ElementalArea${n}_AddButton`,color:"primary",onClick:this.toggle,className:"font-icon-plus"};return r.default.createElement("div",null,r.default.createElement(a.Button,o,l.default._t("ElementAddNewButton.ADD_BLOCK","Add block")),r.default.createElement(e,{placement:"bottom-start",target:o.id,isOpen:this.state.popoverOpen,elementTypes:t,toggle:this.toggle,areaId:n,insertAfterElement:0}))}}t.Component=c,c.defaultProps={},c.propTypes={elementTypes:o.default.arrayOf(i.elementTypeType).isRequired,areaId:o.default.number.isRequired};t.default=(0,d.inject)(["AddElementPopover"],(e=>({AddElementPopoverComponent:e})),(()=>"ElementEditor.ElementList.AddNewButton"))(c)},1305:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r,o=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=d(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),a=(r=n(6935))&&r.__esModule?r:{default:r},l=n(5207),i=n(9367);function d(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(d=function(e){return e?n:t})(e)}class u extends o.PureComponent{render(){const{id:e,fileUrl:t,fileTitle:n,content:r,previewExpanded:a,InlineEditFormComponent:l,SummaryComponent:i,activeTab:d,onFormInit:u,handleLoadingError:s,formDirty:c,broken:f,onFormSchemaSubmitResponse:p,ensureFormRendered:m,formHasRendered:g}=this.props,h=!a&&(m||g),b={"element-editor-editform--collapsed":!a,"element-editor-editform--rendered-not-visible":h};return o.default.createElement("div",{className:"element-editor-content"},!a&&o.default.createElement(i,{content:r,fileUrl:t,fileTitle:n,broken:f}),(a||m||g)&&o.default.createElement(l,{extraClass:b,onClick:e=>e.stopPropagation(),elementId:e,activeTab:d,onFormInit:u,handleLoadingError:s,onFormSchemaSubmitResponse:p,notVisible:h}),c&&o.default.createElement("input",{type:"hidden",name:"change-tracker",className:"element-form-dirty-state",value:"1"}))}}t.Component=u,u.propTypes={id:a.default.string,content:a.default.string,fileUrl:a.default.string,fileTitle:a.default.string,previewExpanded:a.default.bool,SummaryComponent:a.default.elementType,InlineEditFormComponent:a.default.elementType,handleLoadingError:a.default.func,broken:a.default.bool,onFormSchemaSubmitResponse:a.default.func,onFormInit:a.default.func,ensureFormRendered:a.default.bool,formHasRendered:a.default.bool,formDirty:a.default.object},u.defaultProps={};t.default=(0,i.compose)((0,l.inject)(["ElementSummary","ElementInlineEditForm"],((e,t)=>({SummaryComponent:e,InlineEditFormComponent:t})),(()=>"ElementEditor.ElementList.Element")))(u)},1950:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=o(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&{}.hasOwnProperty.call(e,l)){var i=a?Object.getOwnPropertyDescriptor(e,l):null;i&&(i.get||i.set)?Object.defineProperty(r,l,i):r[l]=e[l]}return r.default=e,n&&n.set(e,r),r}(n(1594));function o(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(o=function(e){return e?n:t})(e)}class a extends r.PureComponent{render(){return r.default.createElement("div",{className:"elemental-editor-drag-indicator"},r.default.createElement("div",{className:"elemental-editor-drag-indicator__ball"}))}}t.default=a},584:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.ElementContext=t.Component=void 0;var r=P(n(1594)),o=D(n(6935)),a=n(455),l=n(9791),i=n(9367),d=n(5207),u=D(n(5815)),s=D(n(6923)),c=n(9040),f=n(5381),p=n(2153),m=n(7785),g=P(n(5034)),h=n(1820),b=n(8724),E=n(9306),y=P(n(8918)),_=n(9077),v=n(6721),O=n(1135),T=D(n(5609));function D(e){return e&&e.__esModule?e:{default:e}}function C(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(C=function(e){return e?n:t})(e)}function P(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=C(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}const k=t.ElementContext=(0,r.createContext)(null),w=e=>{const[t,n]=(0,r.useState)(!1),[o,a]=(0,r.useState)(""),[l,i]=(0,r.useState)(!1),[d,c]=(0,r.useState)(e.element.title),[f,p]=(0,r.useState)(!1),[m,g]=(0,r.useState)(!1),[h,E]=(0,r.useState)(!1),[y,_]=(0,r.useState)(!1),[D,C]=(0,r.useState)(!1),[P,w]=(0,r.useState)(!1),[j,S]=(0,r.useState)(!1),{fetchElements:I}=(0,r.useContext)(v.ElementEditorContext);(0,r.useEffect)((()=>{e.connectDragPreview&&e.connectDragPreview((0,b.getEmptyImage)(),{captureDraggingState:!0}),e.formStateExists&&w(!0)}),[]),(0,r.useEffect)((()=>{f&&P&&(p(!1),e.formDirty?(e.submitForm(),_(!0)):E(!0))}),[f,P]),(0,r.useEffect)((()=>{j&&(S(!1),e.dispatchAddFormChanged())}),[j]);const M=()=>u.default.inject(u.default._t("ElementHeader.NOTITLE","Untitled {type} block"),{type:e.type.title}),A=t=>{(t=>{const n=d||M();if(t){const t=u.default.inject(u.default._t("ElementPublishAction.ERROR_NOTIFICATION","Error publishing '{title}'"),{title:n});e.actions.toasts.error(t)}else{const t=u.default.inject(u.default._t("ElementPublishAction.SUCCESS_NOTIFICATION","Published '{title}' successfully"),{title:n});e.actions.toasts.success(t)}})(t),E(!1),_(!1),I().then((()=>{setTimeout((()=>e.dispatchRemoveFormChanged()),250)}))};(0,r.useEffect)((()=>{P&&m&&(e.submitForm(),g(!1))}),[P,m]),(0,r.useEffect)((()=>{if(P&&h){const t=`${(0,O.getConfig)().controllerLink.replace(/\/$/,"")}/api/publish`;T.default.post(t,{ID:e.element.id}).then((()=>A(!1))).catch((()=>A(!0)))}}),[P,h]);const N=t=>{const{tabSetName:n,onActivateTab:r}=e;if(o||a(t),t||o)r(n,t||o);else{r(n,"Main")}},x=r=>{const{type:o,link:a}=e;o.broken||("button"!==r.target.type?!o.inlineEditable||l?window.location=a:n(!t):r.stopPropagation())},{element:R,type:L,areaId:W,HeaderComponent:F,ContentComponent:B,link:H,activeTab:$,connectDragSource:q,connectDropTarget:U,isDragging:V,isOver:K,onDragEnd:G,formDirty:Y}=e;if(!R.id)return null;const Q=(0,s.default)("element-editor__element",{"element-editor__element--broken":L.broken,"element-editor__element--expandable":L.inlineEditable&&!L.broken,"element-editor__element--dragging":V,"element-editor__element--dragged-over":K},(()=>{const{element:t}=e,n="element-editor__element";return t.isPublished?t.isPublished&&!t.isLiveVersion?`${n}--modified`:`${n}--published`:`${n}--draft`})()),J={formDirty:Y,onPublishButtonClick:()=>{p(!0),C(!0)},onSaveButtonClick:()=>{C(!0),g(!0)}},X=U(r.default.createElement("div",{className:Q,onClick:x,onKeyUp:e=>{const{nodeName:t}=e.target;" "!==e.key&&"Enter"!==e.key||["input","textarea"].includes(t.toLowerCase())||x(e)},role:"button",tabIndex:0,title:(e=>e.broken?u.default._t("ElementalElement.ARCHIVE_BROKEN","Archive this block"):u.default.inject(u.default._t("ElementalElement.TITLE","Edit this {type} block"),{type:e.title}))(L),key:R.id},r.default.createElement(k.Provider,{value:J},r.default.createElement(F,{element:R,type:L,areaId:W,expandable:L.inlineEditable,link:H,previewExpanded:t,handleEditTabsClick:t=>{const{activeTab:r}=e;t===r||l||(n(!0),N(t))},activeTab:$,disableTooltip:V,onDragEnd:G}),r.default.createElement(B,{id:R.id,fileUrl:R.blockSchema.fileURL,fileTitle:R.blockSchema.fileTitle,content:((e,t)=>t.broken?e.title?u.default.inject(u.default._t("ElementalElement.BROKEN_DESCRIPTION_TITLE",'This block had the title "{title}". It is broken and will not display on the front-end. You can archive it to remove it from this elemental area.'),{title:e.title}):u.default._t("ElementalElement.BROKEN_DESCRIPTION","This block is broken and will not display on the front-end. You can archive it to remove it from this elemental area."):e.blockSchema.content)(R,L),previewExpanded:t&&!V,ensureFormRendered:D,formHasRendered:P,activeTab:$,handleLoadingError:()=>{i(!0)},broken:L.broken,onFormSchemaSubmitResponse:(t,r)=>{if(t.id.match(/\/schema\/elemental-area\/([0-9]+)/))return e.type.inlineEditable&&n(!0),S(!0),void(y&&_(!1));c(r),y&&(_(!1),E(!0)),h||y||(t=>{const n=t||M(),r=u.default.inject(u.default._t("ElementSaveAction.SUCCESS_NOTIFICATION","Saved '{title}' successfully"),{title:n});e.actions.toasts.success(r)})(r),I()},onFormInit:()=>(e=>{N(e),w(!0)})($)}))));return t?X:q(X)};t.Component=w,w.propTypes={element:a.elementType,type:l.elementTypeType.isRequired,areaId:o.default.number.isRequired,link:o.default.string.isRequired,activeTab:o.default.string,tabSetName:o.default.string,onActivateTab:o.default.func,connectDragSource:o.default.func.isRequired,connectDragPreview:o.default.func.isRequired,connectDropTarget:o.default.func.isRequired,isDragging:o.default.bool.isRequired,isOver:o.default.bool.isRequired,onDragOver:o.default.func,onDragEnd:o.default.func,onDragStart:o.default.func},w.defaultProps={element:null};const j={drop(e,t,n){const{element:r}=e;return{target:r.id,dropSpot:(0,E.isOverTop)(t,n)?"top":"bottom"}},hover(e,t,n){const{element:r,onDragOver:o}=e;o&&o(r,(0,E.isOverTop)(t,n))}};t.default=(0,i.compose)((0,h.DropTarget)("element",j,((e,t)=>({connectDropTarget:e.dropTarget(),isOver:t.isOver()}))),(0,h.DragSource)("element",E.elementDragSource,((e,t)=>({connectDragSource:e.dragSource(),connectDragPreview:e.dragPreview(),isDragging:t.isDragging()}))),(0,c.connect)((function(e,t){const n=t.element.id,r=(0,p.loadElementFormStateName)(n),o=(0,m.loadElementSchemaValue)("schemaUrl",n),a=e.form&&e.form.formSchemas[o]&&e.form.formSchemas[o].schema&&e.form.formSchemas[o].schema.fields.find((e=>"Tabs"===e.component)),l=a&&a.id,i=`element.${r}__${l}`,d=e.unsavedForms.find((e=>e.name===`element.${r}`)),u=e.form&&e.form.formState&&e.form.formState.element&&e.form.formState.element.hasOwnProperty(r);return{tabSetName:l,activeTab:e.tabs&&e.tabs.fields&&e.tabs.fields[i]&&e.tabs.fields[i].activeTab,formDirty:d,formStateExists:u}}),(function(e,t){const n=(0,p.loadElementFormStateName)(t.element.id);return{onActivateTab(t,r){e(g.activateTab(`element.${n}__${t}`,r))},submitForm(){e((0,f.submit)(`element.${n}`))},dispatchAddFormChanged(){e((0,_.addFormChanged)(`element.${n}`))},dispatchRemoveFormChanged(){e((0,_.removeFormChanged)(`element.${n}`))},actions:{toasts:(0,i.bindActionCreators)(y,e)}}})),(0,d.inject)(["ElementHeader","ElementContent"],((e,t)=>({HeaderComponent:e,ContentComponent:t})),(()=>"ElementEditor.ElementList.Element")))(w)},7105:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=f(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=c(n(6935)),a=n(9367),l=n(3556),i=n(5207),d=n(455),u=n(9791),s=c(n(26));function c(e){return e&&e.__esModule?e:{default:e}}function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(f=function(e){return e?n:t})(e)}class p extends r.Component{constructor(e){super(e),this.handleEditTabsClick=this.handleEditTabsClick.bind(this)}handleEditTabsClick(e){const{handleEditTabsClick:t}=this.props;t(e.target.name)}renderEditTabs(){const{editTabs:e,activeTab:t,type:n,expandable:o}=this.props;return!n.broken&&o&&e&&e.length?e.map((e=>{let{name:o,title:a}=e;return r.default.createElement(s.default,{key:o,name:o,title:a,type:n,onClick:this.handleEditTabsClick,active:o===t})})):null}renderDivider(){const{children:e,editTabs:t,expandable:n}=this.props;return n&&t&&t.length&&0!==r.default.Children.count(e)?r.default.createElement(l.DropdownItem,{divider:!0,role:"separator"}):null}render(){const{children:e,id:t,ActionMenuComponent:n}=this.props;return r.default.createElement(n,{id:`element-editor-actions-${t}`,className:"element-editor-header__actions-dropdown",dropdownMenuProps:{right:!0},dropdownToggleClassNames:["element-editor-header__actions-toggle","btn","btn-sm","btn--no-text","font-icon-dot-3"]},this.renderEditTabs(),this.renderDivider(),e)}}t.Component=p,p.propTypes={element:d.elementType,type:u.elementTypeType.isRequired,areaId:o.default.number.isRequired,activeTab:o.default.string,editTabs:o.default.arrayOf(o.default.shape({title:o.default.string,name:o.default.string})),handleEditTabsClick:o.default.func.isRequired,expandable:o.default.bool},p.defaultProps={editTabs:[],expandable:!0};t.default=(0,a.compose)((0,i.inject)(["ActionMenu"],(e=>({ActionMenuComponent:e})),(()=>"ElementEditor.ElementList.Element")))(p)},920:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=c(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=s(n(6935)),a=s(n(7651)),l=n(1820),i=n(455),d=n(9791),u=n(1135);function s(e){return e&&e.__esModule?e:{default:e}}function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(c=function(e){return e?n:t})(e)}class f extends r.Component{render(){const{isDragging:e,element:t,elementTypes:n,currentOffset:o}=this.props;if(!e||!o)return null;const{x:l,y:i}=o,d=`translate(${l}px, ${i}px)`,s={transform:d,WebkitTransform:d},c=(0,u.getElementTypeConfig)(t,n);return r.default.createElement("div",{className:"element-editor-drag-preview",style:s},r.default.createElement(a.default,{element:t,type:c,simple:!0}))}}f.propTypes={element:i.elementType,elementTypes:o.default.arrayOf(d.elementTypeType),isDragging:o.default.bool,currentOffset:o.default.shape({x:o.default.number.isRequired,y:o.default.number.isRequired})};t.default=(0,l.DragLayer)((e=>({element:e.getItem(),currentOffset:e.getSourceClientOffset(),isDragging:e.isDragging()})))(f)},6721:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.ElementEditorContext=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=h(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=g(n(6935)),a=n(5207),l=n(9367),i=n(9791),d=n(9040),u=n(2153),s=n(1820),c=g(n(920)),f=g(n(5799)),p=g(n(5609)),m=n(1135);function g(e){return e&&e.__esModule?e:{default:e}}function h(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(h=function(e){return e?n:t})(e)}const b=t.ElementEditorContext=(0,r.createContext)(null);class E extends r.PureComponent{constructor(e){super(e),this.state={dragTargetElementId:null,dragSpot:null,elements:null,isLoading:!0},this.handleDragOver=this.handleDragOver.bind(this),this.handleDragEnd=this.handleDragEnd.bind(this),this.fetchElements=this.fetchElements.bind(this)}handleDragOver(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=!!e&&e.id;this.setState({dragTargetElementId:n,dragSpot:!1===t?"bottom":"top"})}handleDragEnd(e,t){const n=`${(0,m.getConfig)().controllerLink.replace(/\/$/,"")}/api/sort`;p.default.post(n,{ID:e,afterBlockID:t}).then((()=>this.fetchElements())),this.setState({dragTargetElementId:null,dragSpot:null})}fetchElements(){(!(arguments.length>0&&void 0!==arguments[0])||arguments[0])&&this.setState((e=>({...e,isLoading:!0})));const e=`${(0,m.getConfig)().controllerLink.replace(/\/$/,"")}/api/readElements/${this.props.areaId}`;return p.default.get(e).then((e=>e.json())).then((e=>{this.setState((t=>({...t,elements:e,isLoading:!1})));const t=window.jQuery(".cms-preview");t&&t.entwine("ss.preview")._loadUrl(t.find("iframe").attr("src"))}))}render(){const{fieldName:e,formState:t,ToolbarComponent:n,ListComponent:o,areaId:a,elementTypes:l,isDraggingOver:i,connectDropTarget:d,allowedElements:u,isLoading:s}=this.props,{dragTargetElementId:f,dragSpot:p,elements:m}=this.state;null===m&&this.fetchElements(!1);const g=u.map((e=>l.find((t=>t.class===e)))),h={fetchElements:this.fetchElements};return d(r.default.createElement("div",{className:"element-editor"},r.default.createElement(b.Provider,{value:h},r.default.createElement(n,{elementTypes:g,areaId:a,onDragOver:this.handleDragOver}),r.default.createElement(o,{allowedElementTypes:g,elementTypes:l,areaId:a,onDragOver:this.handleDragOver,onDragStart:this.handleDragStart,onDragEnd:this.handleDragEnd,dragSpot:p,isDraggingOver:i,dragTargetElementId:f,elements:m,isLoading:s}),r.default.createElement(c.default,{elementTypes:l}),r.default.createElement("input",{name:e,type:"hidden",value:JSON.stringify(t)||"",className:"no-change-track"}))))}}t.Component=E,E.propTypes={fieldName:o.default.string,elementTypes:o.default.arrayOf(i.elementTypeType).isRequired,allowedElements:o.default.arrayOf(o.default.string).isRequired,areaId:o.default.number.isRequired,actions:o.default.shape({handleSortBlock:o.default.func})};const y=[f.default,(0,s.DropTarget)("element",{},((e,t)=>({connectDropTarget:e.dropTarget(),isDraggingOver:t.isOver()}))),(0,d.connect)((function(e){const t=(0,u.loadElementFormStateName)("[0-9]+"),n=e.form.formState.element;return n?{formState:Object.keys(n).filter((e=>e.match(t))).reduce(((e,t)=>({...e,[t]:n[t].values})),{})}:{}})),(0,a.inject)(["ElementToolbar","ElementList"],((e,t)=>({ToolbarComponent:e,ListComponent:t})),(()=>"ElementEditor"))];t.default=(0,l.compose)(...y)(E)},2512:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=g(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=m(n(6935)),a=n(455),l=n(9791),i=n(9367),d=n(5207),u=m(n(6923)),s=m(n(5815)),c=n(1820),f=n(9306),p=n(1135);function m(e){return e&&e.__esModule?e:{default:e}}function g(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(g=function(e){return e?n:t})(e)}class h extends r.Component{getDragIndicatorIndex(){const{dragTargetElementId:e,draggedItem:t,elements:n,dragSpot:r}=this.props;return(0,f.getDragIndicatorIndex)(n.map((e=>e.id)),e,t&&t.id,r)}renderBlocks(){const{ElementComponent:e,HoverBarComponent:t,DragIndicatorComponent:n,elements:o,allowedElementTypes:a,elementTypes:l,areaId:i,onDragEnd:d,onDragOver:u,onDragStart:c,isDraggingOver:f}=this.props;if(!o)return null;if(o&&!o.length)return r.default.createElement("div",null,s.default._t("ElementList.ADD_BLOCKS","Add blocks to place your content"));let m=o.map((n=>r.default.createElement("div",{key:n.id},r.default.createElement(e,{element:n,areaId:i,type:(0,p.getElementTypeConfig)(n,l),link:n.blockSchema.actions.edit,onDragOver:u,onDragEnd:d,onDragStart:c}),f||r.default.createElement(t,{key:`create-after-${n.id}`,areaId:i,elementId:n.id,elementTypes:a}))));f||(m=[r.default.createElement(t,{key:0,areaId:i,elementId:0,elementTypes:a})].concat(m));const g=this.getDragIndicatorIndex();return f&&null!==g&&m.splice(g,0,r.default.createElement(n,{key:"DropIndicator"})),m}renderLoading(){const{isLoading:e,LoadingComponent:t}=this.props;return e?r.default.createElement(t,null):null}render(){const{elements:e}=this.props,t=(0,u.default)("elemental-editor-list",{"elemental-editor-list--empty":!e||!e.length});return this.props.connectDropTarget(r.default.createElement("div",{className:t},this.renderLoading(),this.renderBlocks()))}}t.Component=h,h.propTypes={elements:o.default.arrayOf(a.elementType),elementTypes:o.default.arrayOf(l.elementTypeType).isRequired,allowedElementTypes:o.default.arrayOf(l.elementTypeType).isRequired,areaId:o.default.number.isRequired,dragTargetElementId:o.default.oneOfType([o.default.string,o.default.bool]),onDragOver:o.default.func,onDragStart:o.default.func,onDragEnd:o.default.func},h.defaultProps={elements:[],isLoading:!1};const b={drop(e,t){const{elements:n}=e,r=t.getDropResult();if(!r)return{};const o=(0,f.getDragIndicatorIndex)(n.map((e=>e.id)),r.target,t.getItem(),r.dropSpot),a=n[o-1]?n[o-1].id:"0";return{...r,dropAfterID:a}}};t.default=(0,i.compose)((0,c.DropTarget)("element",b,((e,t)=>({connectDropTarget:e.dropTarget(),draggedItem:t.getItem()}))),(0,d.inject)(["Element","Loading","HoverBar","DragPositionIndicator"],((e,t,n,r)=>({ElementComponent:e,LoadingComponent:t,HoverBarComponent:n,DragIndicatorComponent:r})),(()=>"ElementEditor.ElementList")))(h)},7651:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=h(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=g(n(6935)),a=n(3556),l=n(455),i=n(9791),d=n(9367),u=n(5207),s=g(n(5815)),c=g(n(6923)),f=n(1820),p=n(9306),m=n(8724);function g(e){return e&&e.__esModule?e:{default:e}}function h(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(h=function(e){return e?n:t})(e)}class b extends r.Component{constructor(e){super(e),this.toggle=this.toggle.bind(this),this.state={tooltipOpen:!1}}componentDidMount(){const{connectDragPreview:e}=this.props;e&&e((0,m.getEmptyImage)(),{captureDraggingState:!0})}componentDidUpdate(){const{tooltipOpen:e}=this.state,{disableTooltip:t}=this.props;e&&t&&this.setState({tooltipOpen:!1})}getBlockTitle(e,t){return t.broken?s.default.inject(s.default._t("ElementHeader.BROKEN","This element is of obsolete type {type}."),{type:t.obsoleteClassName}):e.title?e.title:s.default.inject(s.default._t("ElementHeader.NOTITLE","Untitled {type} block"),{type:t.title})}toggle(){this.setState((e=>({tooltipOpen:!e.tooltipOpen})))}renderVersionedStateMessage(){const{element:{isLiveVersion:e,isPublished:t}}=this.props;if(t&&e)return null;let n="";const o=["element-editor-header__version-state"];return t?e||(n=s.default._t("ElementHeader.STATE_MODIFIED","Item has unpublished changes"),o.push("element-editor-header__version-state--modified")):(n=s.default._t("ElementHeader.STATE_DRAFT","Item has not been published yet"),o.push("element-editor-header__version-state--draft")),r.default.createElement("span",{className:(0,c.default)(o),title:n})}renderStatusBadge(){const{element:{isLiveVersion:e,isPublished:t}}=this.props;if(t&&e)return null;let n="",o="";const a=["badge"];return t?e||(n=s.default._t("ElementHeader.BADGE_MODIFIED","Modified"),o=s.default._t("ElementHeader.STATE_MODIFIED","Item has unpublished changes"),a.push("status-modified")):(n=s.default._t("ElementHeader.BADGE_DRAFT","Draft"),o=s.default._t("ElementHeader.STATE_DRAFT","Item has not been published yet"),a.push("status-addedtodraft")),r.default.createElement("span",{className:(0,c.default)(a),title:o},n)}render(){const{connectDragSource:e,element:t,type:n,areaId:o,previewExpanded:l,simple:i,disableTooltip:d,activeTab:u,expandable:f,ElementActionsComponent:p,handleEditTabsClick:m}=this.props,g=this.getBlockTitle(t,n),h=(0,c.default)({"element-editor-header__title":!0,"element-editor-header__title--none":!t.title}),b=s.default._t("ElementHeader.EXPAND","Show editable fields"),E=(0,c.default)("element-editor-header",{"element-editor-header--simple":i}),y=(0,c.default)("element-editor-header__icon-container",{"element-editor-header__icon-container--broken":n.broken}),_=(0,c.default)("element-editor-header__expand",{"font-icon-right-open-big":!f,"font-icon-up-open-big":f&&l,"font-icon-down-open-big":f&&!l}),v=`element-icon-${t.id}`,O=r.default.createElement("div",{className:E},r.default.createElement("div",{className:"element-editor-header__drag-handle"},r.default.createElement("i",{className:"font-icon-drag-handle"})),r.default.createElement("div",{className:"element-editor-header__info"},r.default.createElement("div",{className:y},r.default.createElement("i",{className:n.icon,id:v}),this.renderVersionedStateMessage(),!n.broken&&!i&&r.default.createElement(a.Tooltip,{placement:"top",isOpen:this.state.tooltipOpen&&!d,target:v,toggle:this.toggle},n.title)),r.default.createElement("h3",{className:h},g),this.renderStatusBadge()),!i&&r.default.createElement("div",{className:"element-editor-header__actions"},r.default.createElement("div",{role:"none",onClick:e=>e.stopPropagation()},r.default.createElement(p,{element:t,type:n,areaId:o,activeTab:u,editTabs:n.editTabs,handleEditTabsClick:m,expandable:f})),!n.broken&&r.default.createElement("i",{className:_,title:b})));return l?e(O):O}}t.Component=b,b.propTypes={element:l.elementType.isRequired,type:i.elementTypeType.isRequired,areaId:o.default.number,activeTab:o.default.string,simple:o.default.bool,ElementActionsComponent:o.default.elementType,previewExpanded:o.default.bool,disableTooltip:o.default.bool,connectDragSource:o.default.func.isRequired,connectDragPreview:o.default.func.isRequired,onDragEnd:o.default.func},b.defaultProps={expandable:!0};t.default=(0,d.compose)((0,f.DragSource)("element",p.elementDragSource,(e=>({connectDragSource:e.dragSource(),connectDragPreview:e.dragPreview()}))),(0,u.inject)(["ElementActions"],(e=>({ElementActionsComponent:e})),(()=>"ElementEditor.ElementList.Element")))(b)},8953:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=d(n(6935)),a=n(5207),l=n(9791),i=d(n(5815));function d(e){return e&&e.__esModule?e:{default:e}}function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}const s=(0,d(n(7577)).default)("element-editor__hover-bar");function c(e){let{AddElementPopoverComponent:t,elementTypes:n,elementId:o,areaId:a,popoverOpen:l,onToggle:d}=e;const u=`${s("-line")} font-icon-plus-circled`,c=i.default._t("ElementAddNewButton.ADD_BLOCK","Add block"),f={className:s("-area",{"-area--focus":l}),onClick:d,"aria-label":c,title:c,id:`AddBlockHoverBarArea_${a}_${o}`};return r.default.createElement("div",{className:s(""),id:`AddBlockHoverBar_${a}_${o}`},r.default.createElement("button",f,r.default.createElement("span",{className:s("-area-inner")},r.default.createElement("span",{className:u}))),r.default.createElement(t,{placement:"bottom",target:`AddBlockHoverBarArea_${a}_${o}`,isOpen:l,elementTypes:n,toggle:d,container:`#AddBlockHoverBar_${a}_${o}`,areaId:a,insertAfterElement:o}))}class f extends r.Component{constructor(e){super(e),this.toggle=this.toggle.bind(this),this.state={popoverOpen:!1}}toggle(){this.setState((e=>({popoverOpen:!e.popoverOpen})))}render(){const e={...this.state,...this.props,onToggle:this.toggle};return r.default.createElement(c,e)}}t.Component=f,f.propTypes={elementTypes:o.default.arrayOf(l.elementTypeType).isRequired,elementId:o.default.oneOfType([o.default.string,o.default.number]).isRequired,areaId:o.default.oneOfType([o.default.number,o.default.string]).isRequired};t.default=(0,a.inject)(["AddElementPopover"],(e=>({AddElementPopoverComponent:e})),(()=>"ElementEditor.ElementList.HoverBar"))(f)},7333:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=f(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=c(n(6935)),a=c(n(6923)),l=c(n(55)),i=n(7785),d=c(n(5815)),u=n(2153),s=n(9040);function c(e){return e&&e.__esModule?e:{default:e}}function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(f=function(e){return e?n:t})(e)}function p(){return p=Object.assign?Object.assign.bind():function(e){for(var t=1;t{t.match(/PageElements_[0-9]+_Title/)&&(r=e[t])})),n().then((e=>this.props.onFormSchemaSubmitResponse(e,r)))}render(){const{elementId:e,extraClass:t,onClick:n,onFormInit:o,formHasState:d,notVisible:u}=this.props,{loadingError:s}=this.state,c=(0,a.default)("element-editor-editform",t),f={formTag:"form",schemaUrl:(0,i.loadElementSchemaValue)("schemaUrl",e),identifier:"element",refetchSchemaOnMount:!d,onLoadingError:this.handleLoadingError,onSubmit:this.handleSubmit};s&&(f.loading=!1),"function"==typeof o&&(f.onReduxFormInit=o);const m={};return u&&(m["aria-hidden"]="true"),r.default.createElement("div",p({className:c,onClick:n,role:"presentation"},m),r.default.createElement(l.default,f))}}m.propTypes={extraClass:o.default.oneOfType([o.default.string,o.default.object]),onClick:o.default.func,elementId:o.default.string,handleLoadingError:o.default.func,onFormSchemaSubmitResponse:o.default.func,notVisible:o.default.bool};t.default=(0,s.connect)((function(e,t){const n=(0,u.loadElementFormStateName)(t.elementId);return{formHasState:e.form.formState&&e.form.formState.element&&!!e.form.formState.element[n]}}))(m)},7660:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=d(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),o=i(n(6935)),a=i(n(6923)),l=i(n(5815));function i(e){return e&&e.__esModule?e:{default:e}}function d(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(d=function(e){return e?n:t})(e)}class u extends r.PureComponent{render(){const{fileUrl:e,fileTitle:t,content:n,broken:o}=this.props,i=l.default._t("ElementSummary.NO_PREVIEW","No preview available"),d=(0,a.default)("element-editor-summary__content",{"element-editor-summary__content--broken":o});return r.default.createElement("div",{className:"element-editor-summary"},e&&r.default.createElement("img",{className:"element-editor-summary__thumbnail-image",src:e,alt:t}),(n||!e)&&r.default.createElement("p",{className:d},n||i))}}u.defaultProps={},u.propTypes={content:o.default.string,fileUrl:o.default.string,fileTitle:o.default.string,broken:o.default.bool};t.default=u},5563:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var a in e)if("default"!==a&&{}.hasOwnProperty.call(e,a)){var l=o?Object.getOwnPropertyDescriptor(e,a):null;l&&(l.get||l.set)?Object.defineProperty(r,a,l):r[a]=e[a]}return r.default=e,n&&n.set(e,r),r}(n(1594)),a=(r=n(6935))&&r.__esModule?r:{default:r},l=n(5207),i=n(9791),d=n(1820);function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}class s extends o.PureComponent{render(){const{AddNewButtonComponent:e,elementTypes:t,areaId:n,connectDropTarget:r}=this.props;return r(o.default.createElement("div",{className:"element-editor__toolbar"},o.default.createElement(e,{elementTypes:t,areaId:n})))}}s.defaultProps={},s.propTypes={elementTypes:a.default.arrayOf(i.elementTypeType).isRequired,areaId:a.default.number.isRequired,AddNewButtonComponent:a.default.elementType.isRequired,connectDropTarget:a.default.func.isRequired,onDragOver:a.default.func,onDragDrop:a.default.func};const c={hover(e){const{onDragOver:t}=e;t&&t()}};t.default=(0,d.DropTarget)("element",c,(e=>({connectDropTarget:e.dropTarget()})))((0,l.inject)(["ElementAddNewButton"],(e=>({AddNewButtonComponent:e})),(()=>"ElementEditor.ElementToolbar"))(s))},2977:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=l(n(1594)),o=l(n(5815)),a=l(n(6923));function l(e){return e&&e.__esModule?e:{default:e}}t.default=e=>class extends e{getClassName(){const e=[super.getClassName()];return this.props.data.ElementID&&e.unshift("elemental-area__element--historic-inner"),(0,a.default)(e)}render(){const e=this.getLegend(),t=this.props.data.tag||"div",n=this.getClassName(),{data:a}=this.props;return a.ElementID?r.default.createElement(t,{className:n},e,r.default.createElement("div",{className:"elemental-preview elemental-preview--historic"},a.ElementEditLink&&r.default.createElement("a",{className:"elemental-preview__link",href:a.ElementEditLink},r.default.createElement("span",{className:"elemental-preview__link-text"},o.default._t("HistoricElementView.VIEW_BLOCK_HISTORY","Block history")),r.default.createElement("i",{className:"font-icon-angle-right btn--icon-lg elemental-preview__link-caret"})),r.default.createElement("div",{className:"elemental-preview__icon"},r.default.createElement("i",{className:a.ElementIcon})),r.default.createElement("div",{className:"elemental-preview__detail"},r.default.createElement("h3",null,a.ElementTitle," ",r.default.createElement("small",null,a.ElementType)))),this.props.children):super.render()}}},6573:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=l(n(1594)),o=n(3556),a=l(n(2623));function l(e){return e&&e.__esModule?e:{default:e}}t.default=(0,a.default)((e=>{const{children:t}=e,n=r.default.Children.toArray(r.default.Children.map(t,((t,n)=>{const o={};return 0===n?(o.id=e.id,o.title=null):1===n&&(o.noHolder=!0),r.default.cloneElement(t,o)})));return e.readOnly?r.default.createElement("div",{className:"text-checkbox-group-field--read-only"},n):1===n.length?n[0]:r.default.createElement(o.InputGroup,{className:"text-checkbox-group-field"},n[0],r.default.createElement(o.InputGroupAddon,{addonType:"append"},r.default.createElement(o.InputGroupText,null,n[1])))}))},51:function(e,t,n){var r=u(n(1669)),o=u(n(1594)),a=n(5145),l=n(5207),i=n(1135),d=n(5381);function u(e){return e&&e.__esModule?e:{default:e}}const s=()=>{setTimeout((()=>{const{store:e}=window.ss;e&&e.dispatch((0,d.destroy)(...Object.keys(e.getState().form.formState.element||{}).map((e=>`element.${e}`))))}),0)};r.default.entwine("ss",(e=>{e(".js-injector-boot .element-editor__container").entwine({ReactRoot:null,onmatch(){const e=(0,l.loadComponent)("ElementEditor",{}),t=this.data("schema"),n=(0,i.getConfig)().elementTypes,r={fieldName:this.attr("name"),areaId:t["elemental-area-id"],allowedElements:t["allowed-elements"],elementTypes:n};let d=this.getReactRoot();d||(d=(0,a.createRoot)(this[0]),this.setReactRoot(d)),d.render(o.default.createElement(e,r))},onunmatch(){e(".cms-edit-form").data("hasValidationErrors")||s();const t=this.getReactRoot();t&&(t.unmount(),this.setReactRoot(null))},"from .cms-edit-form":{onaftersubmitform(t,n){const r=JSON.parse(n.xhr.responseText).ValidationResult;JSON.parse(r.replace(/<\/?script[^>]*?>/g,"")).isValid?(e(".cms-edit-form").data("hasValidationErrors",!1),s()):e(".cms-edit-form").data("hasValidationErrors",!0)}}}),e(".js-injector-boot .element-editor__container .element-form-dirty-state").entwine({onmatch(){e(".cms-edit-form").trigger("change")},onunmatch(){e(".cms-edit-form").trigger("change")}}),e(".cms-edit-form").entwine({getChangeTrackerOptions(){const t=void 0===this.entwineData("ChangeTrackerOptions");let n=this._super();return t&&(n=e.extend({},n),n.ignoreFieldSelector+=", .elementalarea :input:not(.element-form-dirty-state)",this.setChangeTrackerOptions(n)),n}})}))},9306:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.isOverTop=t.getDragIndicatorIndex=t.elementDragSource=void 0;var r=n(4518);t.isOverTop=(e,t)=>{const n=e.getClientOffset(),o=(0,r.findDOMNode)(t).getBoundingClientRect();return n.y{if(null===t||!n)return null;let o=t?e.findIndex((e=>e===t)):0;const a=e.findIndex((e=>e===n));return"bottom"===r&&(o+=1),a===o||a+1===o?null:o};t.elementDragSource={beginDrag(e){return e.element},endDrag(e,t){const{onDragEnd:n}=e,r=t.getDropResult();if(!n||!r||!r.dropAfterID)return;const o=t.getItem().id,{dropAfterID:a}=r;o!==a&&n(o,a)}}},7577:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=(r=n(6923))&&r.__esModule?r:{default:r};t.default=e=>function(){const t=t=>`${e}${t}`;for(var n=arguments.length,r=new Array(n),a=0;a!(!e&&""!==e)&&("object"==typeof e?Array.isArray(e)?e.map(t):Object.entries(e).reduce(((e,n)=>{let[r,o]=n;return Object.assign({},e,{[t(r)]:o})}),{}):t(e))));return(0,o.default)(...l)}},1135:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.getElementTypeConfig=t.getConfig=void 0;var r,o=(r=n(6177))&&r.__esModule?r:{default:r};const a=()=>o.default.getSection("DNADesign\\Elemental\\Controllers\\ElementalAreaController");t.getConfig=a;t.getElementTypeConfig=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=e.blockSchema.typeName;let r=(Array.isArray(t)?t:a().elementTypes).find((e=>e.class===n||e.name===n));return e.obsoleteClassName&&(r=Object.assign({obsoleteClassName:e.obsoleteClassName},r),Object.preventExtensions(r)),r}},2153:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.loadElementFormStateName=void 0;var r,o=(r=n(6177))&&r.__esModule?r:{default:r};t.loadElementFormStateName=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;const t=o.default.getSection("DNADesign\\Elemental\\Controllers\\ElementalAreaController").form.elementForm.formNameTemplate;return e?t.replace("{id}",e):t}},7785:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.loadElementSchemaValue=void 0;var r,o=(r=n(6177))&&r.__esModule?r:{default:r};t.loadElementSchemaValue=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=o.default.getSection("DNADesign\\Elemental\\Controllers\\ElementalAreaController").form.elementForm[e]||"";return t?`${n}/${t}`:n}},3337:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=l(n(1594)),o=l(n(5609)),a=n(1135);function l(e){return e&&e.__esModule?e:{default:e}}t.default=e=>t=>{const n={...t};return n.hasOwnProperty("actions")||(n.actions={}),n.actions.revertToVersion=(e,t,n,r)=>{const l=`${(0,a.getConfig)().controllerLink.replace(/\/$/,"")}/api/revert`;return o.default.post(l,{ID:e,fromVersion:t,fromStage:n,toStage:r})},r.default.createElement(e,n)}},455:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.elementType=void 0;var r,o=(r=n(6935))&&r.__esModule?r:{default:r};t.elementType=o.default.shape({id:o.default.string.isRequired,title:o.default.string,blockSchema:o.default.object,inlineEditable:o.default.bool,published:o.default.bool,liveVersion:o.default.bool,version:o.default.number})},9791:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.elementTypeType=void 0;var r,o=(r=n(6935))&&r.__esModule?r:{default:r};t.elementTypeType=o.default.shape({name:o.default.string,title:o.default.string,icon:o.default.string,inlineEditable:o.default.bool,editTabs:o.default.arrayOf(o.default.shape({title:o.default.string,name:o.default.string})),config:o.default.object})},5609:function(e){e.exports=Backend},6177:function(e){e.exports=Config},2623:function(e){e.exports=FieldHolder},55:function(e){e.exports=FormBuilderLoader},5207:function(e){e.exports=Injector},6935:function(e){e.exports=PropTypes},1594:function(e){e.exports=React},1820:function(e){e.exports=ReactDND},8724:function(e){e.exports=ReactDNDHtml5Backend},4518:function(e){e.exports=ReactDom},5145:function(e){e.exports=ReactDomClient},9040:function(e){e.exports=ReactRedux},3556:function(e){e.exports=Reactstrap},9367:function(e){e.exports=Redux},5381:function(e){e.exports=ReduxForm},5034:function(e){e.exports=TabsActions},8918:function(e){e.exports=ToastsActions},9077:function(e){e.exports=UnsavedFormsActions},6923:function(e){e.exports=classnames},5815:function(e){e.exports=i18n},1669:function(e){e.exports=jQuery},5799:function(e){e.exports=withDragDropContext}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var a=t[r]={exports:{}};return e[r](a,a.exports,n),a.exports}n(51),n(2038)}(); \ No newline at end of file diff --git a/client/src/boot/registerTransforms.js b/client/src/boot/registerTransforms.js index 75cfd145..8fd98f43 100644 --- a/client/src/boot/registerTransforms.js +++ b/client/src/boot/registerTransforms.js @@ -1,9 +1,6 @@ import Injector from 'lib/Injector'; -import readOneBlockQuery from 'state/history/readOneBlockQuery'; import HistoricElementViewFactory from 'components/HistoricElementView/HistoricElementView'; -import revertToBlockVersionMutation from 'state/history/revertToBlockVersionMutation'; -import readBlocksForAreaQuery from 'state/editor/readBlocksForAreaQuery'; -import addElementToArea from 'state/editor/addElementMutation'; +import revertToBlockVersionRequest from 'state/history/revertToBlockVersionRequest'; import ArchiveAction from 'components/ElementActions/ArchiveAction'; import DuplicateAction from 'components/ElementActions/DuplicateAction'; import SaveAction from 'components/ElementActions/SaveAction'; @@ -25,50 +22,14 @@ export default () => { } ); - Injector.transform( - 'elements-history', - (updater) => { - // Add content block history to the HistoryViewer - updater.component( - 'HistoryViewer.Form_ItemEditForm', - readOneBlockQuery, - 'ElementHistoryViewer' - ); - } - ); - Injector.transform( 'blocks-history-revert', (updater) => { - // Add block element revert GraphQL mutation to the HistoryViewerToolbar + // Add revertToVersion() to props.actions on HistoryViewerToolbar updater.component( 'HistoryViewerToolbar.VersionedAdmin.HistoryViewer.Element.HistoryViewerVersionDetail', - revertToBlockVersionMutation, - 'BlockRevertMutation' - ); - } - ); - - 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' + revertToBlockVersionRequest, + 'BlockRevertRequest' ); } ); diff --git a/client/src/components/ElementActions/ArchiveAction.js b/client/src/components/ElementActions/ArchiveAction.js index 101198a7..25c1d95d 100644 --- a/client/src/components/ElementActions/ArchiveAction.js +++ b/client/src/components/ElementActions/ArchiveAction.js @@ -1,40 +1,40 @@ /* global window */ -import React from 'react'; -import { compose } from 'redux'; +import React, { useContext } from 'react'; 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 { fetchElements } = 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') { - preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src')); - } - }); + if (!window.confirm(archiveMessage)) { + return; } + const id = props.element.id; + const url = `${getConfig().controllerLink.replace(/\/$/, '')}/api/archive`; + backend.post(url, { + ID: id, + }) + .then(() => fetchElements()); }; const disabled = props.element.canDelete !== undefined && !props.element.canDelete; @@ -61,4 +61,4 @@ const ArchiveAction = (MenuComponent) => (props) => { export { ArchiveAction as Component }; -export default compose(archiveBlockMutation, ArchiveAction); +export default ArchiveAction; diff --git a/client/src/components/ElementActions/DuplicateAction.js b/client/src/components/ElementActions/DuplicateAction.js index f1996aab..df0ed68d 100644 --- a/client/src/components/ElementActions/DuplicateAction.js +++ b/client/src/components/ElementActions/DuplicateAction.js @@ -1,14 +1,17 @@ /* global window */ -import React from 'react'; -import { compose } from 'redux'; +import React, { useContext } from 'react'; 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 { fetchElements } = useContext(ElementEditorContext); + if (props.type.broken) { // Don't allow this action for a broken element. return ( @@ -18,15 +21,12 @@ 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')); - }); - } + const id = props.element.id; + const url = `${getConfig().controllerLink.replace(/\/$/, '')}/api/duplicate`; + backend.post(url, { + ID: id, + }) + .then(() => fetchElements()); }; const disabled = props.element.canCreate !== undefined && !props.element.canCreate; @@ -53,4 +53,4 @@ const DuplicateAction = (MenuComponent) => (props) => { export { DuplicateAction as Component }; -export default compose(duplicateBlockMutation, DuplicateAction); +export default DuplicateAction; diff --git a/client/src/components/ElementActions/UnpublishAction.js b/client/src/components/ElementActions/UnpublishAction.js index a8bc506d..d2a22377 100644 --- a/client/src/components/ElementActions/UnpublishAction.js +++ b/client/src/components/ElementActions/UnpublishAction.js @@ -1,14 +1,17 @@ /* global window */ -import React from 'react'; -import { compose } from 'redux'; +import React, { useContext } from 'react'; import AbstractAction from 'components/ElementActions/AbstractAction'; -import unpublishBlockMutation from 'state/editor/unpublishBlockMutation'; import i18n from 'i18n'; +import backend from 'lib/Backend'; +import { ElementEditorContext } from 'components/ElementEditor/ElementEditor'; +import { getConfig } from 'state/editor/elementConfig'; /** * Adds the elemental menu action to unpublish a published block */ const UnpublishAction = (MenuComponent) => (props) => { + const { fetchElements } = useContext(ElementEditorContext); + if (props.type.broken) { // Don't allow this action for a broken element. return ( @@ -16,51 +19,42 @@ const UnpublishAction = (MenuComponent) => (props) => { ); } - const { element, type, actions: { handleUnpublishBlock } } = props; - - const handleClick = (event) => { - event.stopPropagation(); - const { jQuery: $ } = window; + const reportUnpublicationStatus = (type, title, success) => { const noTitle = i18n.inject( - i18n._t( - 'ElementHeader.NOTITLE', - 'Untitled {type} block' - ), - { type: type.title } + i18n._t('ElementHeader.NOTITLE', 'Untitled {type} block'), + { type } + ); + const successMessage = i18n.inject( + i18n._t('ElementUnpublishAction.SUCCESS_NOTIFICATION', 'Removed \'{title}\' from the published page'), + { title: title || noTitle } + ); + const errorMessage = i18n.inject( + i18n._t('ElementUnpublishAction.ERROR_NOTIFICATION', 'Error unpublishing \'{title}\''), + { title: title || noTitle } ); + window.jQuery.noticeAdd({ + text: success ? successMessage : errorMessage, + stay: false, + type: success ? 'success' : 'error', + }); + }; + + const unpublishElement = () => { + const id = props.element.id; + const url = `${getConfig().controllerLink.replace(/\/$/, '')}/api/unpublish`; + return backend.post(url, { + ID: id, + }) + .then(() => fetchElements()); + }; - if (handleUnpublishBlock) { - handleUnpublishBlock(element.id) - .then(() => { - const preview = $('.cms-preview'); - preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src')); + const { element, type } = props; - $.noticeAdd({ - text: i18n.inject( - i18n._t( - 'ElementUnpublishAction.SUCCESS_NOTIFICATION', - 'Removed \'{title}\' from the published page' - ), - { title: element.title || noTitle } - ), - stay: false, - type: 'success' - }); - }) - .catch(() => { - $.noticeAdd({ - text: i18n.inject( - i18n._t( - 'ElementUnpublishAction.ERROR_NOTIFICATION', - 'Error unpublishing \'{title}\'' - ), - { title: element.title || noTitle } - ), - stay: false, - type: 'error' - }); - }); - } + const handleClick = (event) => { + event.stopPropagation(); + unpublishElement() + .then(() => reportUnpublicationStatus(type.title, element.title, true)) + .catch(() => reportUnpublicationStatus(type.title, element.title, false)); }; const disabled = props.element.canUnpublish !== undefined && !props.element.canUnpublish; @@ -87,4 +81,4 @@ const UnpublishAction = (MenuComponent) => (props) => { export { UnpublishAction as Component }; -export default compose(unpublishBlockMutation, UnpublishAction); +export default UnpublishAction; diff --git a/client/src/components/ElementActions/tests/ArchiveAction-test.js b/client/src/components/ElementActions/tests/ArchiveAction-test.js index bd21978d..44336f0c 100644 --- a/client/src/components/ElementActions/tests/ArchiveAction-test.js +++ b/client/src/components/ElementActions/tests/ArchiveAction-test.js @@ -4,6 +4,26 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { Component as ArchiveAction } from '../ArchiveAction'; +import { ElementEditorContext } from '../../ElementEditor/ElementEditor'; + +let resolveMockPostRequest; + +jest.mock('lib/Backend', () => ({ + post: () => new Promise((resolve) => { + resolveMockPostRequest = resolve; + }) +})); + +const sectionConfigKey = 'DNADesign\\Elemental\\Controllers\\ElementalAreaController'; +window.ss.config = { + SecurityID: 1234567890, + sections: [ + { + name: sectionConfigKey, + controllerLink: 'my/test/endpoint', + }, + ], +}; const WrappedComponent = (props) =>
{props.children}
; const ActionComponent = ArchiveAction(WrappedComponent); @@ -16,9 +36,7 @@ function makeProps(obj = {}) { element: { id: 123, isPublished: true, - blockSchema: { type: 'Test' } }, - isPublished: true, actions: { handleArchiveBlock: () => {} }, @@ -27,44 +45,70 @@ function makeProps(obj = {}) { }; } +function makeProviderProps(obj = {}) { + return { + value: { + fetchElements: () => {}, + ...obj, + } + }; +} + test('ArchiveAction renders the title and class', () => { - const { container } = render(); + const { container } = render( + + + + ); expect(container.querySelector('button.element-editor__actions-archive').textContent).toBe('Archive'); }); -test('ArchiveAction does not archive when declining the confirmation', () => { +test('ArchiveAction does not archive when declining the confirmation', async () => { + resolveMockPostRequest = 'lorem ipsum'; global.confirm = () => false; - const mockMutation = jest.fn(() => new Promise((resolve) => { resolve(); })); - const { container } = render(); + const { container } = render( + + + + ); fireEvent.click(container.querySelector('button.element-editor__actions-archive')); - expect(mockMutation).not.toHaveBeenCalled(); + // mock backend post method was never called, therefore resolveMockPostRequest value unchanged + expect(resolveMockPostRequest).toBe('lorem ipsum'); }); -test('ArchiveAction archives when accepting the confirmation', () => { +test('ArchiveAction archives when accepting the confirmation', async () => { global.confirm = () => true; - const mockMutation = jest.fn(() => new Promise((resolve) => { resolve(); })); - const { container } = render(); + let resolvePromise; + const myPromise = new Promise((resolve) => { + resolvePromise = resolve; + }); + const fetchElements = jest.fn(() => { + resolvePromise(); + }); + const { container } = render( + + + + ); fireEvent.click(container.querySelector('button.element-editor__actions-archive')); - expect(mockMutation).toHaveBeenCalled(); + resolveMockPostRequest(); + await Promise.all([myPromise]); + expect(fetchElements).toHaveBeenCalled(); }); test('ArchiveAction indicates that the block will be sent to archive when it is unpublished', () => { const mockConfirm = jest.fn(); global.confirm = mockConfirm; - const { container } = render(); + const { container } = render( + + + + ); fireEvent.click(container.querySelector('button.element-editor__actions-archive')); expect(mockConfirm).toHaveBeenCalledWith('Are you sure you want to send this block to the archive?'); }); @@ -72,30 +116,44 @@ test('ArchiveAction indicates that the block will be sent to archive when it is test('ArchiveAction indicates that the block will be sent to archive when it is published', () => { const mockConfirm = jest.fn(); global.confirm = mockConfirm; - const { container } = render(); + const { container } = render( + + + + ); fireEvent.click(container.querySelector('button.element-editor__actions-archive')); expect(mockConfirm).toHaveBeenCalledWith('Warning: This block will be unpublished before being sent to the archive. Are you sure you want to proceed?'); }); test('ArchiveAction is disabled when user doesn\'t have correct permissions', () => { - const { container } = render(); + const { container } = render( + + + + ); expect(container.querySelector('button.element-editor__actions-archive').disabled).toBe(true); }); test('ArchiveAction renders a button even when block is broken', () => { - const { container } = render(); + const { container } = render( + + + + ); expect(container.querySelectorAll('button.element-editor__actions-archive')).toHaveLength(1); }); diff --git a/client/src/components/ElementActions/tests/DuplicateAction-test.js b/client/src/components/ElementActions/tests/DuplicateAction-test.js index 4ec0f761..56b75960 100644 --- a/client/src/components/ElementActions/tests/DuplicateAction-test.js +++ b/client/src/components/ElementActions/tests/DuplicateAction-test.js @@ -4,6 +4,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { Component as DuplicateAction } from '../DuplicateAction'; +import { ElementEditorContext } from '../../ElementEditor/ElementEditor'; const WrappedComponent = (props) =>
{props.children}
; const ActionComponent = DuplicateAction(WrappedComponent); @@ -27,30 +28,49 @@ function makeProps(obj = {}) { }; } +function makeProviderProps(obj = {}) { + return { + value: { + fetchElements: () => {}, + ...obj, + } + }; +} + test('DuplicateAction renders a button', () => { - const { container } = render(); + const { container } = render( + + + + ); expect(container.querySelectorAll('button.element-editor__actions-duplicate')).toHaveLength(1); }); test('DuplicateAction is disabled when user doesn\'t have correct permissions', () => { const { container } = render( - + + + ); expect(container.querySelector('button.element-editor__actions-duplicate').disabled).toBe(true); }); test('DuplicateAction does not render a button when block is broken', () => { const { container } = render( - + + + ); expect(container.querySelectorAll('button.element-editor__actions-duplicate')).toHaveLength(0); }); diff --git a/client/src/components/ElementActions/tests/PublishAction-test.js b/client/src/components/ElementActions/tests/PublishAction-test.js index 47b0b3bd..1bf0fa4e 100644 --- a/client/src/components/ElementActions/tests/PublishAction-test.js +++ b/client/src/components/ElementActions/tests/PublishAction-test.js @@ -3,6 +3,7 @@ import React from 'react'; import { fireEvent, render } from '@testing-library/react'; import { Component as PublishAction } from '../PublishAction'; +import { ElementEditorContext } from '../../ElementEditor/ElementEditor'; import { ElementContext } from '../../ElementEditor/Element'; window.jQuery = { @@ -28,61 +29,85 @@ function makeProps(obj = {}) { }; } -function makeProviderValue(obj = {}) { +function makeElementEditorProviderProps(obj = {}) { return { - doPublishElement: false, - formHasRendered: false, - onAfterPublish: () => {}, - onPublishButtonClick: () => {}, - ...obj, + value: { + fetchElements: () => {}, + ...obj, + } + }; +} + +function makeElementProviderProps(obj = {}) { + return { + value: { + doPublishElement: false, + formHasRendered: false, + onAfterPublish: () => {}, + onPublishButtonClick: () => {}, + ...obj, + } }; } const WrappedComponent = (props) =>
{props.children}
; const ActionComponent = PublishAction(WrappedComponent); -const ProvidedActionComponent = (props) => ( - - - -); test('PublishAction renders the title and class', () => { - const { container } = render(); + const { container } = render( + + + + + + ); expect(container.querySelector('button.element-editor__actions-publish').textContent).toBe('Publish'); }); test('PublishAction returns null when is the live version', () => { const { container } = render( - + + + + + ); expect(container.querySelector('button.element-editor__actions-publish')).toBe(null); }); test('PublishAction is disabled when user doesn\'t have correct permissions', () => { const { container } = render( - + + + + + ); expect(container.querySelector('button.element-editor__actions-publish').disabled).toBe(true); }); test('PublishAction does not render a button when block is broken', () => { const { container } = render( - + + + + + ); expect(container.querySelectorAll('button.element-editor__actions-publish')).toHaveLength(0); }); @@ -90,12 +115,14 @@ test('PublishAction does not render a button when block is broken', () => { test('Clicking button calls onPublishButtonClick', () => { const onPublishButtonClick = jest.fn(); const { container } = render( - - - + + + + + ); fireEvent.click(container.querySelector('button.element-editor__actions-publish')); expect(onPublishButtonClick).toHaveBeenCalled(); diff --git a/client/src/components/ElementActions/tests/SaveAction-test.js b/client/src/components/ElementActions/tests/SaveAction-test.js index 528c0b73..89257293 100644 --- a/client/src/components/ElementActions/tests/SaveAction-test.js +++ b/client/src/components/ElementActions/tests/SaveAction-test.js @@ -4,6 +4,7 @@ import React from 'react'; import { fireEvent, render } from '@testing-library/react'; import { Component as SaveAction } from '../SaveAction'; +import { ElementEditorContext } from '../../ElementEditor/ElementEditor'; import { ElementContext } from '../../ElementEditor/Element'; function makeProps(obj = {}) { @@ -26,61 +27,83 @@ function makeProps(obj = {}) { }; } -function makeProviderValue(obj = {}) { +function makeElementEditorProviderProps(obj = {}) { return { - formHasRendered: false, - onAfterSave: () => {}, - doSaveElement: false, - onSaveButtonClick: () => {}, - submitForm: () => {}, - formDirty: true, - ...obj, + value: { + fetchElements: () => {}, + ...obj, + } + }; +} + +function makeElementProviderProps(obj = {}) { + return { + value: { + formHasRendered: false, + onAfterSave: () => {}, + doSaveElement: false, + onSaveButtonClick: () => {}, + submitForm: () => {}, + formDirty: true, + ...obj, + } }; } const WrappedComponent = (props) =>
{props.children}
; const ActionComponent = SaveAction(WrappedComponent); -const ProvidedActionComponent = (props) => ( - - - -); + test('SaveAction does not render a button when block is expandable and not formDirty', () => { const { container } = render( - - - + + + + + ); expect(container.querySelectorAll('button.element-editor__actions-save')).toHaveLength(0); }); + test('SaveAction renders a button when block is expandable and formDirty', () => { const { container } = render( - + + + + + ); expect(container.querySelectorAll('button.element-editor__actions-save')).toHaveLength(1); }); test('SaveAction does not render a button when block is not expandable', () => { const { container } = render( - + + + + + ); expect(container.querySelectorAll('button.element-editor__actions-save')).toHaveLength(0); }); test('SaveAction does not render a button when block is broken', () => { const { container } = render( - + + + + + ); expect(container.querySelectorAll('button.element-editor__actions-save')).toHaveLength(0); }); @@ -88,12 +111,14 @@ test('SaveAction does not render a button when block is broken', () => { test('Clicking button calls onSaveButtonClick', () => { const onSaveButtonClick = jest.fn(); const { container } = render( - - - + + + + + ); fireEvent.click(container.querySelector('button.element-editor__actions-save')); expect(onSaveButtonClick).toHaveBeenCalled(); diff --git a/client/src/components/ElementActions/tests/UnpublishAction-test.js b/client/src/components/ElementActions/tests/UnpublishAction-test.js index edd3dcac..49a95576 100644 --- a/client/src/components/ElementActions/tests/UnpublishAction-test.js +++ b/client/src/components/ElementActions/tests/UnpublishAction-test.js @@ -4,11 +4,31 @@ import React from 'react'; import { fireEvent, render } from '@testing-library/react'; import { Component as UnpublishAction } from '../UnpublishAction'; +import { ElementEditorContext } from '../../ElementEditor/ElementEditor'; window.jQuery = { noticeAdd: jest.fn() }; +let resolveMockPostRequest; + +jest.mock('lib/Backend', () => ({ + post: () => new Promise((resolve) => { + resolveMockPostRequest = resolve; + }) +})); + +const sectionConfigKey = 'DNADesign\\Elemental\\Controllers\\ElementalAreaController'; +window.ss.config = { + SecurityID: 1234567890, + sections: [ + { + name: sectionConfigKey, + controllerLink: 'my/test/endpoint', + }, + ], +}; + function makeProps(obj = {}) { return { title: 'My unpublish action', @@ -26,63 +46,85 @@ function makeProps(obj = {}) { }; } +function makeProviderProps(obj = {}) { + return { + value: { + fetchElements: () => {}, + ...obj, + } + }; +} + const WrappedComponent = (props) =>
{props.children}
; const ActionComponent = UnpublishAction(WrappedComponent); test('UnpublishAction renders the title and class', () => { const { container } = render( - + + + ); expect(container.querySelector('button.element-editor__actions-unpublish').textContent).toBe('Unpublish'); }); test('UnpublishAction returns null when is not published', () => { const { container } = render( - + + + ); expect(container.querySelectorAll('button')).toHaveLength(0); }); -test('UnpublishAction calls the unpublish mutation', () => { - const mockMutation = jest.fn(() => new Promise((resolve) => { resolve(); })); +test('UnpublishAction calls unpublish endpoint and fetchElements', async () => { + let resolvePromise; + const myPromise = new Promise((resolve) => { + resolvePromise = resolve; + }); + const fetchElements = jest.fn(() => { + resolvePromise(); + }); const { container } = render( - + + + ); fireEvent.click(container.querySelector('button.element-editor__actions-unpublish')); - expect(mockMutation).toHaveBeenCalledWith(123); + resolveMockPostRequest(); + await Promise.all([myPromise]); + expect(fetchElements).toHaveBeenCalled(); }); test('UnpublishAction is disabled when user doesn\'t have correct permissions', () => { const { container } = render( - + + + ); expect(container.querySelector('button.element-editor__actions-unpublish').disabled).toBe(true); }); test('UnpublishAction does not render a button when block is broken', () => { const { container } = render( - + + + ); expect(container.querySelectorAll('button.element-editor__actions-unpublish')).toHaveLength(0); }); diff --git a/client/src/components/ElementEditor/AddElementPopover.js b/client/src/components/ElementEditor/AddElementPopover.js index cb6cb312..f4a3c137 100644 --- a/client/src/components/ElementEditor/AddElementPopover.js +++ b/client/src/components/ElementEditor/AddElementPopover.js @@ -6,6 +6,8 @@ import classNames from 'classnames'; import { inject } from 'lib/Injector'; import { elementTypeType } from 'types/elementTypeType'; import i18n from 'i18n'; +import backend from 'lib/Backend'; +import { ElementEditorContext } from 'components/ElementEditor/ElementEditor'; /** * The AddElementPopover component used in the context of an ElementEditor shows the @@ -16,27 +18,31 @@ class AddElementPopover extends Component { super(props); this.handleToggle = this.handleToggle.bind(this); + AddElementPopover.contextType = ElementEditorContext; } /** - * click handler that preserves the details of what was clicked - * @param {object} elementType in the shape of types/elmementTypeType - * @returns {function} + * #rpc + * - call add element to area endpoint (areaID, elementType, insertAfterElementID) + * - then call read blocks from area endpoint (areaID) + * - also then update the preview via jquery/entwine */ getElementButtonClickHandler(elementType) { return (event) => { - const { - actions: { handleAddElementToArea }, - insertAfterElement - } = this.props; - event.preventDefault(); - handleAddElementToArea(elementType.class, insertAfterElement).then( - () => { + backend.post('/admin/elemental-area/api/add/', { + elementClass: elementType.class, + elementalAreaID: this.props.areaId, + insertAfterElementID: this.props.insertAfterElement, + }) + .then(() => { + const { fetchElements } = this.context; + return fetchElements(); + }) + .then(() => { const preview = window.jQuery('.cms-preview'); preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src')); - } - ); + }); this.handleToggle(); }; } diff --git a/client/src/components/ElementEditor/Element.js b/client/src/components/ElementEditor/Element.js index df124704..8e315948 100644 --- a/client/src/components/ElementEditor/Element.js +++ b/client/src/components/ElementEditor/Element.js @@ -1,7 +1,6 @@ /* global window */ -import React, { useState, useEffect, createContext } from 'react'; -import { useMutation } from '@apollo/client'; +import React, { useState, useEffect, useContext, createContext } from 'react'; import PropTypes from 'prop-types'; import { elementType } from 'types/elementType'; import { elementTypeType } from 'types/elementTypeType'; @@ -13,14 +12,15 @@ import { connect } from 'react-redux'; import { submit } from 'redux-form'; import { loadElementFormStateName } from 'state/editor/loadElementFormStateName'; import { loadElementSchemaValue } from 'state/editor/loadElementSchemaValue'; -import { publishBlockMutation } from 'state/editor/publishBlockMutation'; -import { query as readBlocksForAreaQuery } from 'state/editor/readBlocksForAreaQuery'; import * as TabsActions from 'state/tabs/TabsActions'; import { DragSource, DropTarget } from 'react-dnd'; import { getEmptyImage } from 'react-dnd-html5-backend'; import { elementDragSource, isOverTop } from 'lib/dragHelpers'; import * as toastsActions from 'state/toasts/ToastsActions'; import { addFormChanged, removeFormChanged } from 'state/unsavedForms/UnsavedFormsActions'; +import { ElementEditorContext } from 'components/ElementEditor/ElementEditor'; +import { getConfig } from 'state/editor/elementConfig'; +import backend from 'lib/Backend'; export const ElementContext = createContext(null); @@ -40,7 +40,7 @@ const Element = (props) => { const [ensureFormRendered, setEnsureFormRendered] = useState(false); const [formHasRendered, setFormHasRendered] = useState(false); const [doDispatchAddFormChanged, setDoDispatchAddFormChanged] = useState(false); - const [publishBlock] = useMutation(publishBlockMutation); + const { fetchElements } = useContext(ElementEditorContext); useEffect(() => { if (props.connectDragPreview) { @@ -111,29 +111,20 @@ const Element = (props) => { } }; - // This will trigger a graphql request that will cause this - // element to re-render including any updated title and versioned badge - const refetchElementalArea = () => window.ss.apolloClient.queryManager.refetchQueries({ - include: [{ - query: readBlocksForAreaQuery, - variables: { id: props.areaId } - }] - }); - const handleAfterPublish = (wasError) => { showPublishedElementToast(wasError); setDoPublishElement(false); setDoPublishElementAfterSave(false); - // Ensure that formDirty becomes falsey after publishing - // We need to call at a later render rather than straight away or redux-form may override this - // and set the form state to dirty under certain conditions - // setTimeout is a hackish way to do this, though I'm not sure how else we can do this - // The core issue is that redux-form will detect changes when a form is hydrated for the first - // time under certain conditions, specifically during a behat test when trying to publish a closed - // block when presumably the apollo cache is empty (or something like that). This happens late and - // there are no hooks/callbacks available after this happens the input onchange handlers are fired - Promise.all(refetchElementalArea()) + fetchElements() .then(() => { + // Ensure that formDirty becomes falsey after publishing + // We need to call at a later render rather than straight away or redux-form may override this + // and set the form state to dirty under certain conditions + // setTimeout is a hackish way to do this, though I'm not sure how else we can do this + // The core issue is that redux-form will detect changes when a form is hydrated for the first + // time under certain conditions, specifically during a behat test when trying to publish a closed + // block when presumably the old apollo cache was empty (or something like that). This happens late and + // there are no hooks/callbacks available after this happens the input onchange handlers are fired setTimeout(() => props.dispatchRemoveFormChanged(), 250); }); }; @@ -149,7 +140,10 @@ const Element = (props) => { // Publish action useEffect(() => { if (formHasRendered && doPublishElement) { - publishBlock({ variables: { blockId: props.element.id } }) + const url = `${getConfig().controllerLink.replace(/\/$/, '')}/api/publish`; + backend.post(url, { + ID: props.element.id, + }) .then(() => handleAfterPublish(false)) .catch(() => handleAfterPublish(true)); } @@ -335,7 +329,7 @@ const Element = (props) => { if (!doPublishElement && !doPublishElementAfterSave) { showSavedElementToast(title); } - refetchElementalArea(); + fetchElements(); }; const { diff --git a/client/src/components/ElementEditor/ElementEditor.js b/client/src/components/ElementEditor/ElementEditor.js index 4373cbd9..c607697c 100644 --- a/client/src/components/ElementEditor/ElementEditor.js +++ b/client/src/components/ElementEditor/ElementEditor.js @@ -1,5 +1,5 @@ /* global window */ -import React, { PureComponent } from 'react'; +import React, { PureComponent, createContext } from 'react'; import PropTypes from 'prop-types'; import { inject } from 'lib/Injector'; import { compose } from 'redux'; @@ -7,10 +7,12 @@ import { elementTypeType } from 'types/elementTypeType'; import { connect } from 'react-redux'; import { loadElementFormStateName } from 'state/editor/loadElementFormStateName'; import { DropTarget } from 'react-dnd'; -import sortBlockMutation from 'state/editor/sortBlockMutation'; import ElementDragPreview from 'components/ElementEditor/ElementDragPreview'; import withDragDropContext from 'lib/withDragDropContext'; -import { createSelector } from 'reselect'; +import backend from 'lib/Backend'; +import { getConfig } from 'state/editor/elementConfig'; + +export const ElementEditorContext = createContext(null); /** * The ElementEditor is used in the CMS to manage a list or nested lists of @@ -23,10 +25,13 @@ class ElementEditor extends PureComponent { this.state = { dragTargetElementId: null, dragSpot: null, + elements: null, + isLoading: true, }; this.handleDragOver = this.handleDragOver.bind(this); this.handleDragEnd = this.handleDragEnd.bind(this); + this.fetchElements = this.fetchElements.bind(this); } /** @@ -50,18 +55,16 @@ class ElementEditor extends PureComponent { /** * Hook for ReactDND triggered when a drag source is dropped onto a drag target. * - * This will fire the GraphQL mutation for sorting and reset any state updates - * * @param sourceId * @param afterId */ handleDragEnd(sourceId, afterId) { - const { actions: { handleSortBlock }, areaId } = this.props; - - handleSortBlock(sourceId, afterId, areaId).then(() => { - const preview = window.jQuery('.cms-preview'); - preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src')); - }); + const url = `${getConfig().controllerLink.replace(/\/$/, '')}/api/sort`; + backend.post(url, { + ID: sourceId, + afterBlockID: afterId, + }) + .then(() => this.fetchElements()); this.setState({ dragTargetElementId: null, @@ -69,6 +72,33 @@ class ElementEditor extends PureComponent { }); } + /** + * Make an API call to readAll elements endpoint (areaID) + */ + fetchElements(doSetLoadingState = true) { + if (doSetLoadingState) { + this.setState(prevState => ({ + ...prevState, + isLoading: true, + })); + } + const url = `${getConfig().controllerLink.replace(/\/$/, '')}/api/readElements/${this.props.areaId}`; + return backend.get(url) + .then(response => response.json()) + .then(responseJson => { + this.setState(prevState => ({ + ...prevState, + elements: responseJson, + isLoading: false, + })); + // refresh preview + const preview = window.jQuery('.cms-preview'); + if (preview) { + preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src')); + } + }); + } + render() { const { fieldName, @@ -80,39 +110,55 @@ class ElementEditor extends PureComponent { isDraggingOver, connectDropTarget, allowedElements, + isLoading, } = this.props; - const { dragTargetElementId, dragSpot } = this.state; + const { dragTargetElementId, dragSpot, elements } = this.state; + + if (elements === null) { + this.fetchElements(false); + } // Map the allowed elements because we want to retain the sort order provided by that array. const allowedElementTypes = allowedElements.map(className => elementTypes.find(type => type.class === className) ); + // Need to convert this to a functional component in order to resolve the following eslint warning: + // warning The 'providerValue' object (at line 124) passed as the value prop to the Context provider (at line 127) changes every render. To fix this consider wrapping it in a useMemo hook + // eslint-disable-next-line react/jsx-no-constructed-context-values + const providerValue = { + fetchElements: this.fetchElements, + }; + return connectDropTarget(
- - - - + + + + + +
); } @@ -128,44 +174,27 @@ ElementEditor.propTypes = { }), }; -const defaultElementFormState = {}; - -// Use a memoization to prevent mapStateToProps() re-rendering on formstate changes -// Any formstate change, including unrelated ones such as from another FormBuilderLoader component -// will trigger the ElementalEditor to re-render -const elementFormSelector = createSelector([ - (state) => { - const elementFormState = state.form.formState.element; - - if (!elementFormState) { - // This needs to a reference to the defaultElementFormState variable rather than a new object - // or redux will think the state has changed and cause the component to re-render - return defaultElementFormState; - } - - return elementFormState; - }], (elementFormState) => { +function mapStateToProps(state) { const formNamePattern = loadElementFormStateName('[0-9]+'); + const elementFormState = state.form.formState.element; + + if (!elementFormState) { + return {}; + } - const filteredElementFormState = Object.keys(elementFormState) + const formState = Object.keys(elementFormState) .filter(key => key.match(formNamePattern)) .reduce((accumulator, key) => ({ ...accumulator, [key]: elementFormState[key].values }), {}); - return filteredElementFormState; -}); - -function mapStateToProps(state) { - // Memoize form state and value changes - const formState = elementFormSelector(state); - return { formState }; } export { ElementEditor as Component }; -export default compose( + +const params = [ withDragDropContext, DropTarget('element', {}, (connector, monitor) => ({ connectDropTarget: connector.dropTarget(), @@ -179,6 +208,7 @@ export default compose( ListComponent, }), () => 'ElementEditor' - ), - sortBlockMutation -)(ElementEditor); + ) +]; + +export default compose(...params)(ElementEditor); diff --git a/client/src/components/ElementEditor/ElementList.js b/client/src/components/ElementEditor/ElementList.js index 8d462a07..9acb806a 100644 --- a/client/src/components/ElementEditor/ElementList.js +++ b/client/src/components/ElementEditor/ElementList.js @@ -12,9 +12,9 @@ import { getElementTypeConfig } from 'state/editor/elementConfig'; class ElementList extends Component { getDragIndicatorIndex() { - const { dragTargetElementId, draggedItem, blocks, dragSpot } = this.props; + const { dragTargetElementId, draggedItem, elements, dragSpot } = this.props; return getDragIndicatorIndex( - blocks.map(element => element.id), + elements.map(element => element.id), dragTargetElementId, draggedItem && draggedItem.id, dragSpot @@ -23,15 +23,14 @@ class ElementList extends Component { /** * Renders a list of Element components, each with an elementType object - * of data mapped into it. The data is provided by a GraphQL HOC registered - * in registerTransforms.js. + * of data mapped into it. */ renderBlocks() { const { ElementComponent, HoverBarComponent, DragIndicatorComponent, - blocks, + elements, allowedElementTypes, elementTypes, areaId, @@ -41,16 +40,16 @@ class ElementList extends Component { isDraggingOver, } = this.props; - // Blocks can be either null or an empty array - if (!blocks) { + // Elements can be either null or an empty array + if (!elements) { return null; } - if (blocks && !blocks.length) { + if (elements && !elements.length) { return
{i18n._t('ElementList.ADD_BLOCKS', 'Add blocks to place your content')}
; } - let output = blocks.map((element) => ( + let output = elements.map((element) => (
; } return null; } render() { - const { blocks } = this.props; + const { elements } = this.props; + const listClassNames = classNames( 'elemental-editor-list', - { 'elemental-editor-list--empty': !blocks || !blocks.length } + { 'elemental-editor-list--empty': !elements || !elements.length } ); return this.props.connectDropTarget( @@ -121,10 +124,9 @@ class ElementList extends Component { } ElementList.propTypes = { - blocks: PropTypes.arrayOf(elementType), + elements: PropTypes.arrayOf(elementType), elementTypes: PropTypes.arrayOf(elementTypeType).isRequired, allowedElementTypes: PropTypes.arrayOf(elementTypeType).isRequired, - loading: PropTypes.bool, areaId: PropTypes.number.isRequired, dragTargetElementId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), onDragOver: PropTypes.func, @@ -133,15 +135,15 @@ ElementList.propTypes = { }; ElementList.defaultProps = { - blocks: [], - loading: false, + elements: [], + isLoading: false, }; export { ElementList as Component }; const elementListTarget = { drop(props, monitor) { - const { blocks } = props; + const { elements } = props; const elementTargetDropResult = monitor.getDropResult(); if (!elementTargetDropResult) { @@ -149,12 +151,12 @@ const elementListTarget = { } const dropIndex = getDragIndicatorIndex( - blocks.map(element => element.id), + elements.map(element => element.id), elementTargetDropResult.target, monitor.getItem(), elementTargetDropResult.dropSpot, ); - const dropAfterID = blocks[dropIndex - 1] ? blocks[dropIndex - 1].id : '0'; + const dropAfterID = elements[dropIndex - 1] ? elements[dropIndex - 1].id : '0'; return { ...elementTargetDropResult, diff --git a/client/src/components/ElementEditor/tests/Element-test.js b/client/src/components/ElementEditor/tests/Element-test.js index 9304cb88..854bc8a2 100644 --- a/client/src/components/ElementEditor/tests/Element-test.js +++ b/client/src/components/ElementEditor/tests/Element-test.js @@ -2,9 +2,9 @@ /* global jest, test, describe, it, expect */ import React from 'react'; -import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'; import { render } from '@testing-library/react'; import { Component as Element } from '../Element'; +import { ElementEditorContext } from '../ElementEditor'; function makeProps(obj = {}) { return { @@ -40,81 +40,98 @@ function makeProps(obj = {}) { }; } +function makeProviderProps(obj = {}) { + return { + value: { + fetchElements: () => {}, + ...obj, + } + }; +} + test('Element should render the HeaderComponent and the ContentComponent', () => { - const client = new ApolloClient({ cache: new InMemoryCache() }); - const { container } = render(); + const { container } = render( + + + + ); expect(container.querySelectorAll('.element-editor__element .test-header')).toHaveLength(1); expect(container.querySelectorAll('.element-editor__element .test-content')).toHaveLength(1); }); test('Element should not render at all if no ID is given', () => { - const client = new ApolloClient({ cache: new InMemoryCache() }); const { container } = render( - + + + ); expect(container.querySelectorAll('.element-editor__element')).toHaveLength(0); }); test('Element should render even if the element is broken', () => { - const client = new ApolloClient({ cache: new InMemoryCache() }); const { container } = render( - + + + ); expect(container.querySelectorAll('.element-editor__element .test-header')).toHaveLength(1); expect(container.querySelectorAll('.element-editor__element .test-content')).toHaveLength(1); }); test('Element getVersionedStateClassName() should identify draft elements', () => { - const client = new ApolloClient({ cache: new InMemoryCache() }); const { container } = render( - + + + ); expect(container.querySelector('.element-editor__element').classList.contains('element-editor__element--draft')).toBe(true); }); test('Element getVersionedStateClassName() should identify modified elements', () => { - const client = new ApolloClient({ cache: new InMemoryCache() }); const { container } = render( - + + + ); expect(container.querySelectorAll('.element-editor__element--modified')).toHaveLength(1); }); test('Element getVersionedStateClassName() should identify published elements', () => { - const client = new ApolloClient({ cache: new InMemoryCache() }); const { container } = render( - + + + ); expect(container.querySelectorAll('.element-editor__element--published')).toHaveLength(1); }); diff --git a/client/src/components/ElementEditor/tests/ElementEditor-test.js b/client/src/components/ElementEditor/tests/ElementEditor-test.js index 39f9213b..93045074 100644 --- a/client/src/components/ElementEditor/tests/ElementEditor-test.js +++ b/client/src/components/ElementEditor/tests/ElementEditor-test.js @@ -5,6 +5,24 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { Component as ElementEditor } from '../ElementEditor'; +jest.mock('lib/Backend', () => ({ + get: () => Promise.resolve({ json: () => [] }), +})); + +const sectionConfigKey = 'DNADesign\\Elemental\\Controllers\\ElementalAreaController'; +window.ss.config = { + SecurityID: 1234567890, + sections: [ + { + name: sectionConfigKey, + controllerLink: 'my/test/endpoint', + }, + ], +}; + +const jQuery = jest.fn(); +window.jQuery = jQuery; + function makeProps(obj = {}) { return { ToolbarComponent: ({ elementTypes }) =>
type.class).join(',')} />, @@ -53,28 +71,31 @@ function makeProps(obj = {}) { }; } -test('ElementEditor should render ElementList and Toolbar', () => { +test('ElementEditor should render ElementList and Toolbar', async () => { const { container } = render(); + await screen.findByTestId('test-toolbar'); expect(container.querySelectorAll('.test-list')).toHaveLength(1); expect(container.querySelectorAll('[data-testid="test-toolbar"]')).toHaveLength(1); }); -test('ElementEditor should filter all element types by those allowed for this editor', () => { +test('ElementEditor should filter all element types by those allowed for this editor', async () => { render( ); + await screen.findByTestId('test-toolbar'); expect(screen.getByTestId('test-toolbar').getAttribute('data-elementtypes')).toBe('Test\\Class\\Aye'); }); -test('ElementEditor should retain the order specified by the allowed elements config', () => { +test('ElementEditor should retain the order specified by the allowed elements config', async () => { render( ); + await screen.findByTestId('test-toolbar'); expect(screen.getByTestId('test-toolbar').getAttribute('data-elementtypes')).toBe('Test\\Class\\Bee,Test\\Class\\TestElement'); }); diff --git a/client/src/components/ElementEditor/tests/ElementList-test.js b/client/src/components/ElementEditor/tests/ElementList-test.js index 94628c8d..186cc298 100644 --- a/client/src/components/ElementEditor/tests/ElementList-test.js +++ b/client/src/components/ElementEditor/tests/ElementList-test.js @@ -17,7 +17,7 @@ const elementTypes = [ function makeProps(obj = {}) { return { key: '1', - blocks: [ + elements: [ { id: '1', title: 'Title', @@ -53,7 +53,7 @@ function makeProps(obj = {}) { }; } -test('ElementList renders elements when blocks are provided as props', () => { +test('ElementList renders elements when elements are provided as props', () => { const { container } = render(); expect(container.querySelectorAll('.test-element')).toHaveLength(2); expect(container.querySelectorAll('.test-loading')).toHaveLength(0); @@ -63,8 +63,8 @@ test('ElementList renders a loading component', () => { const { container } = render( ); @@ -76,8 +76,8 @@ test('ElementList renders a placeholder message when no elements are provided as const { container } = render( ); diff --git a/client/src/legacy/ElementEditor/entwine.js b/client/src/legacy/ElementEditor/entwine.js index 41f34aad..9bac27e5 100644 --- a/client/src/legacy/ElementEditor/entwine.js +++ b/client/src/legacy/ElementEditor/entwine.js @@ -9,14 +9,9 @@ import { getConfig } from 'state/editor/elementConfig'; import { destroy } from 'redux-form'; /** - * Reset the Apollo and Redux stores holding data relating to elemental inline edit forms + * Reset the Redux store holding data relating to elemental inline edit forms */ const resetStores = () => { - // After page level saves we need to reload all the blocks from the server. We can remove - // this if we can figure out a way to optimistically update the apollo cache. See: - // https://github.com/dnadesign/silverstripe-elemental/pull/439#issuecomment-428773370 - window.ss.apolloClient.resetStore(); - // Defer playing with redux store setTimeout(() => { // After the page submit we want to destroy the form values so it's reloaded. We can't @@ -82,7 +77,7 @@ jQuery.entwine('ss', ($) => { const validationResultPjax = JSON.parse(data.xhr.responseText).ValidationResult; const validationResult = JSON.parse(validationResultPjax.replace(/<\/?script[^>]*?>/g, '')); - // Reset redux store if form is succesfully submitted so apollo to refetches element data + // Reset redux store if form is succesfully submitted // Do not reset if there are any validation errors because we want redux to hydrate the // form, rather than then refetching which will return a value from the database. // Instead the user should still see any modfied value they just entered. diff --git a/client/src/state/editor/addElementMutation.js b/client/src/state/editor/addElementMutation.js deleted file mode 100644 index e61223fc..00000000 --- a/client/src/state/editor/addElementMutation.js +++ /dev/null @@ -1,42 +0,0 @@ -import { graphql } from '@apollo/client/react/hoc'; -import gql from 'graphql-tag'; -import { query as readBlocksQuery, config as readBlocksConfig } from './readBlocksForAreaQuery'; - -// GraphQL query for deleting a specific block -const mutation = gql` -mutation AddElementToArea($className: String!, $elementalAreaID: ID!, $afterElementID: ID) { - addElementToArea( - className: $className, - elementalAreaID: $elementalAreaID, - afterElementID: $afterElementID - ) { - id - } -} -`; - -const config = { - props: ({ mutate, ownProps: { actions, areaId } }) => { - const handleAddElementToArea = (className, afterElementID) => mutate({ - variables: { className, elementalAreaID: areaId, afterElementID }, - }); - - return { - actions: { - ...actions, - handleAddElementToArea, - }, - }; - }, - options: ({ areaId }) => ({ - // Refetch versions after mutation is completed - refetchQueries: [{ - query: readBlocksQuery, - variables: readBlocksConfig.options({ areaId }).variables - }] - }) -}; - -export { mutation, config }; - -export default graphql(mutation, config); diff --git a/client/src/state/editor/archiveBlockMutation.js b/client/src/state/editor/archiveBlockMutation.js deleted file mode 100644 index 2b12fb39..00000000 --- a/client/src/state/editor/archiveBlockMutation.js +++ /dev/null @@ -1,36 +0,0 @@ -import { graphql } from '@apollo/client/react/hoc'; -import gql from 'graphql-tag'; -import { config as readBlocksConfig, query as readBlocksQuery } from './readBlocksForAreaQuery'; - -// GraphQL query for deleting a specific block -const mutation = gql` -mutation ArchiveBlock($blockId: ID!) { - deleteBlocks(ids: [$blockId]) -} -`; - -const config = { - props: ({ mutate, ownProps: { actions } }) => { - const handleArchiveBlock = (blockId) => mutate({ - variables: { blockId }, - }); - - return { - actions: { - ...actions, - handleArchiveBlock, - }, - }; - }, - options: ({ areaId }) => ({ - // Refetch versions after mutation is completed - refetchQueries: [{ - query: readBlocksQuery, - variables: readBlocksConfig.options({ areaId }).variables - }] - }), -}; - -export { mutation, config }; - -export default graphql(mutation, config); diff --git a/client/src/state/editor/duplicateBlockMutation.js b/client/src/state/editor/duplicateBlockMutation.js deleted file mode 100644 index 5e36496a..00000000 --- a/client/src/state/editor/duplicateBlockMutation.js +++ /dev/null @@ -1,38 +0,0 @@ -import { graphql } from '@apollo/client/react/hoc'; -import gql from 'graphql-tag'; -import { config as readBlocksConfig, query as readBlocksQuery } from './readBlocksForAreaQuery'; - -// GraphQL query for duplicating a specific block -const mutation = gql` -mutation DuplicateBlock($blockId: ID!) { - duplicateBlock(id: $blockId) { - id - } -} -`; - -const config = { - props: ({ mutate, ownProps: { actions } }) => { - const handleDuplicateBlock = (blockId) => mutate({ - variables: { blockId }, - }); - - return { - actions: { - ...actions, - handleDuplicateBlock, - }, - }; - }, - options: ({ areaId }) => ({ - // Refetch versions after mutation is completed - refetchQueries: [{ - query: readBlocksQuery, - variables: readBlocksConfig.options({ areaId }).variables - }] - }), -}; - -export { mutation, config }; - -export default graphql(mutation, config); diff --git a/client/src/state/editor/publishBlockMutation.js b/client/src/state/editor/publishBlockMutation.js deleted file mode 100644 index 9f54f2e8..00000000 --- a/client/src/state/editor/publishBlockMutation.js +++ /dev/null @@ -1,9 +0,0 @@ -import gql from 'graphql-tag'; - -export const publishBlockMutation = gql` -mutation PublishBlock($blockId:ID!) { - publishBlock(id: $blockId) { - id - } -} -`; diff --git a/client/src/state/editor/readBlocksForAreaQuery.js b/client/src/state/editor/readBlocksForAreaQuery.js deleted file mode 100644 index 9b9488c3..00000000 --- a/client/src/state/editor/readBlocksForAreaQuery.js +++ /dev/null @@ -1,65 +0,0 @@ -import { graphql } from '@apollo/client/react/hoc'; -import gql from 'graphql-tag'; - -// GraphQL query for retrieving the current state of elements for an area. The results of the query -// must be set to the "blocks" prop on the component that this HOC is applied to for binding -// implementation. -const query = gql` -query ReadBlocksForArea($id:ID!) { - readOneElementalArea(filter: { id: { eq: $id } }, versioning: { - mode: DRAFT - }){ - elements { - id - title - blockSchema - obsoleteClassName - isLiveVersion - isPublished - version - canCreate - canPublish - canUnpublish - canDelete - } - } -} -`; - -const config = { - options({ areaId }) { - return { - variables: { - id: areaId, - } - }; - }, - props( - { - data: { - error, - readOneElementalArea, - loading: networkLoading, - }, - } - ) { - let blocks = null; - if (readOneElementalArea) { - // Remove the GraphQL pagination keys - blocks = readOneElementalArea.elements; - } - - const errors = error && error.graphQLErrors && - error.graphQLErrors.map((graphQLError) => graphQLError.message); - - return { - loading: networkLoading || !blocks, - blocks, - graphQLErrors: errors, - }; - }, -}; - -export { query, config }; - -export default graphql(query, config); diff --git a/client/src/state/editor/sortBlockMutation.js b/client/src/state/editor/sortBlockMutation.js deleted file mode 100644 index 71dedc0b..00000000 --- a/client/src/state/editor/sortBlockMutation.js +++ /dev/null @@ -1,92 +0,0 @@ -import { graphql } from '@apollo/client/react/hoc'; -import gql from 'graphql-tag'; -import { query as readBlocksQuery, config as readBlocksConfig } from './readBlocksForAreaQuery'; - -// GraphQL query for changing the sort order of blocks -const mutation = gql` -mutation SortBlockMutation($blockId:ID!, $afterBlockId:ID!) { - sortBlock( - id: $blockId - afterBlockID: $afterBlockId - ) { - id - isLiveVersion - isPublished - } -} -`; - -const config = { - props: ({ mutate, ownProps: { actions } }) => { - const handleSortBlock = (blockId, afterBlockId, areaId) => mutate({ - variables: { - blockId, - afterBlockId, - }, - optimisticResponse: { - sortBlock: { - id: blockId, - isLiveVersion: false, - isPublished: false, - __typename: 'Block', - }, - }, - update: (store, { data: { sortBlock: updatedElementData } }) => { - const variables = readBlocksConfig.options({ areaId }).variables; - const cachedData = store.readQuery({ query: readBlocksQuery, variables }); - - // Query returns a deeply nested object. Explicit reconstruction via spreads is too verbose. - // This is an alternative, relatively efficient way to deep clone - const newData = JSON.parse(JSON.stringify(cachedData)); - let blocks = newData.readOneElementalArea.elements; - // Find the block we reordered - const movedBlockIndex = blocks.findIndex(block => block.id === blockId); - // Keep it - const movedBlock = blocks[movedBlockIndex]; - // Update the moved block with the new details returned in the GraphQL response - Object.entries(updatedElementData).forEach(([key, value]) => { - // Skip the type name as this is always returned but should never change - if (key === '__typename') { - return; - } - - movedBlock[key] = value; - }); - // Remove the moved block - blocks.splice(movedBlockIndex, 1); - // If the target is 0, it's moving to the start - if (afterBlockId === '0') { - blocks.unshift(movedBlock); - } else { - // Else, find the block we inserted after - let targetBlockIndex = blocks.findIndex(block => block.id === afterBlockId); - - // If we can't find the block, it must be the one we're trying to move - put it back! - if (targetBlockIndex === -1) { - targetBlockIndex = movedBlockIndex - 1; - } - - // Add it back after the target - const end = blocks.slice(targetBlockIndex + 1); - blocks = blocks.slice(0, targetBlockIndex + 1); - blocks.push(movedBlock); - blocks = blocks.concat(end); - } - - // Add it back to the full result - newData.readOneElementalArea.elements = blocks; - store.writeQuery({ query: readBlocksQuery, data: newData, variables }); - }, - }); - return { - actions: { - ...actions, - handleSortBlock, - }, - }; - }, -}; - -export { mutation, config }; - -export default graphql(mutation, config); diff --git a/client/src/state/editor/unpublishBlockMutation.js b/client/src/state/editor/unpublishBlockMutation.js deleted file mode 100644 index 261bcb1f..00000000 --- a/client/src/state/editor/unpublishBlockMutation.js +++ /dev/null @@ -1,45 +0,0 @@ -import { graphql } from '@apollo/client/react/hoc'; -import gql from 'graphql-tag'; -import { config as readBlocksConfig, query as readBlocksQuery } from './readBlocksForAreaQuery'; - -// GraphQL query for unpublishing a specific block -const mutation = gql` -mutation UnpublishBlock($blockId:ID!) { - unpublishBlock( - id: $blockId - ) { - id - } -} -`; - -const config = { - props: ({ mutate, ownProps: { actions } }) => { - const handleUnpublishBlock = (blockId, fromStage, toStage, fromVersion) => mutate({ - variables: { - blockId, - fromStage, - toStage, - fromVersion - }, - }); - - return { - actions: { - ...actions, - handleUnpublishBlock, - }, - }; - }, - options: ({ areaId }) => ({ - // Refetch versions after mutation is completed - refetchQueries: [{ - query: readBlocksQuery, - variables: readBlocksConfig.options({ areaId }).variables - }] - }), -}; - -export { mutation, config }; - -export default graphql(mutation, config); diff --git a/client/src/state/history/readOneBlockQuery.js b/client/src/state/history/readOneBlockQuery.js deleted file mode 100644 index af211a61..00000000 --- a/client/src/state/history/readOneBlockQuery.js +++ /dev/null @@ -1,96 +0,0 @@ -import { graphql } from '@apollo/client/react/hoc'; -import gql from 'graphql-tag'; - -// GraphQL query for retrieving the version history of a specific block. The results of -// the query must be set to the "versions" prop on the component that this HOC is -// applied to for binding implementation. -const query = gql` -query ReadHistoryViewerBlock ($block_id: ID!, $limit: Int!, $offset: Int!) { - readOneBlock( - versioning: { - mode: LATEST - }, - filter: { id: { eq: $block_id } } - ) { - id - versions (limit: $limit, offset: $offset, sort: { version: DESC }) { - pageInfo { - totalCount - } - nodes { - version - absoluteLink - author { - firstName - surname - } - publisher { - firstName - surname - } - published - liveVersion - latestDraftVersion - lastEdited - } - } - } -} -`; - -const config = { - options({ recordId, limit, page }) { - return { - variables: { - limit, - offset: ((page || 1) - 1) * limit, - block_id: recordId, - } - }; - }, - props( - { - data: { - error, - refetch, - readOneBlock, - loading: networkLoading, - }, - ownProps: { - actions = { - versions: {} - }, - limit, - recordId, - }, - } - ) { - const versions = readOneBlock || null; - - const errors = error && error.graphQLErrors && - error.graphQLErrors.map((graphQLError) => graphQLError.message); - - return { - loading: networkLoading || !versions, - versions, - graphQLErrors: errors, - actions: { - ...actions, - versions: { - ...versions, - goToPage(page) { - refetch({ - offset: ((page || 1) - 1) * limit, - limit, - block_id: recordId, - }); - } - }, - }, - }; - }, -}; - -export { query, config }; - -export default graphql(query, config); diff --git a/client/src/state/history/revertToBlockVersionMutation.js b/client/src/state/history/revertToBlockVersionMutation.js deleted file mode 100644 index ef2de656..00000000 --- a/client/src/state/history/revertToBlockVersionMutation.js +++ /dev/null @@ -1,43 +0,0 @@ -import { graphql } from '@apollo/client/react/hoc'; -import gql from 'graphql-tag'; - -const mutation = gql` -mutation revertBlockToVersion($id:ID!, $fromStage:VersionedStage!, $toStage:VersionedStage!, $fromVersion:Int!) { - copyBlockToStage(input: { - id: $id - fromVersion: $fromVersion - fromStage: $fromStage - toStage: $toStage - }) { - id - } -} -`; - -const config = { - props: ({ mutate, ownProps: { actions } }) => { - const revertToVersion = (id, fromVersion, fromStage, toStage) => mutate({ - variables: { - id, - fromVersion, - fromStage, - toStage, - }, - }); - - return { - actions: { - ...actions, - revertToVersion, - }, - }; - }, - options: { - // Refetch versions after mutation is completed - refetchQueries: ['ReadHistoryViewerBlock'] - } -}; - -export { mutation, config }; - -export default graphql(mutation, config); diff --git a/client/src/state/history/revertToBlockVersionRequest.js b/client/src/state/history/revertToBlockVersionRequest.js new file mode 100644 index 00000000..3e2d6b23 --- /dev/null +++ b/client/src/state/history/revertToBlockVersionRequest.js @@ -0,0 +1,22 @@ +import React from 'react'; +import backend from 'lib/Backend'; +import { getConfig } from 'state/editor/elementConfig'; + +const revertToBlockVersionRequest = (HistoryViewerVersionDetailComponent) => (props) => { + const newProps = { ...props }; + if (!newProps.hasOwnProperty('actions')) { + newProps.actions = {}; + } + newProps.actions.revertToVersion = (id, fromVersion, fromStage, toStage) => { + const url = `${getConfig().controllerLink.replace(/\/$/, '')}/api/revert`; + return backend.post(url, { + ID: id, + fromVersion, + fromStage, + toStage + }); + }; + return ; +}; + +export default revertToBlockVersionRequest; diff --git a/client/src/types/elementTypeType.js b/client/src/types/elementTypeType.js index fcc28308..e80dc1a9 100644 --- a/client/src/types/elementTypeType.js +++ b/client/src/types/elementTypeType.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; // Describes the structure of an element type: const elementTypeType = PropTypes.shape({ - // The GraphQL typeName of the element + // The typeName of the element name: PropTypes.string, // The "name" of the type (eg. "Content") title: PropTypes.string, diff --git a/composer.json b/composer.json index 1d469bfe..1359a63a 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,6 @@ "silverstripe/admin": "^3", "silverstripe/versioned": "^3", "silverstripe/versioned-admin": "^3", - "silverstripe/graphql": "^6", "symbiote/silverstripe-gridfieldextensions": "^5", "silverstripe/vendor-plugin": "^2" }, diff --git a/package.json b/package.json index b0475451..24b1524d 100644 --- a/package.json +++ b/package.json @@ -33,12 +33,9 @@ }, "homepage": "https://github.com/dnadesign/silverstripe-elemental#readme", "dependencies": { - "@apollo/client": "^3.7.1", "@popperjs/core": "^2.11.6", "bootstrap": "^4.6.2", "classnames": "^2.3.2", - "graphql": "^16.8.1", - "graphql-tag": "^2.12.6", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dnd": "^5.0.0", diff --git a/src/Controllers/ElementalAreaController.php b/src/Controllers/ElementalAreaController.php index 9cad61e9..8eb88056 100644 --- a/src/Controllers/ElementalAreaController.php +++ b/src/Controllers/ElementalAreaController.php @@ -15,6 +15,12 @@ use SilverStripe\Forms\FormAction; use SilverStripe\Forms\FieldList; use SilverStripe\Control\Controller; +use InvalidArgumentException; +use DNADesign\Elemental\Models\ElementalArea; +use DNADesign\Elemental\Services\ReorderElements; +use SilverStripe\Control\Director; +use SilverStripe\Versioned\Versioned; +use Exception; /** * Controller for "ElementalArea" - handles loading and saving of in-line edit forms in an elemental area in admin @@ -31,14 +37,308 @@ class ElementalAreaController extends CMSMain 'elementForm/$ItemID' => 'elementForm', 'POST api/saveForm/$ID' => 'apiSaveForm', '$FormName/field/$FieldName' => 'formAction', + // new endpoints + 'GET api/readElements/$elementalAreaID!' => 'apiReadElements', + 'POST api/add' => 'apiAdd', + 'POST api/sort' => 'apiSort', + 'POST api/duplicate' => 'apiDuplicate', + 'POST api/archive' => 'apiArchive', + 'POST api/publish' => 'apiPublish', + 'POST api/unpublish' => 'apiUnpublish', + // todo + 'POST api/revert' => 'apiRevert', ]; private static $allowed_actions = [ 'elementForm', 'apiSaveForm', 'formAction', + // new endpoints + 'apiReadElements', + 'apiAdd', + 'apiSort', + 'apiDuplicate', + 'apiArchive', + 'apiPublish', + 'apiUnpublish', + 'apiRevert', ]; + private function jsonResponse(int $statusCode = 200, ?array $data = null, string $message = ''): HTTPResponse + { + $response = $this->getResponse(); + $response->setStatusCode($statusCode); + $response->addHeader('Content-Type', 'application/json'); + $body = ''; + if (!is_null($data)) { + $body = json_encode($data); + } elseif ($message) { + $body = json_encode(['message' => $message]); + } + $response->setBody($body); + return $response; + } + + // === + + private function getPostData() + { + $request = $this->getRequest(); + $postData = json_decode($request->getBody(), true); + return $postData; + } + + // VersionedResolver resolveCopyToStage() + public function apiRevert(): HTTPResponse + { + $postData = $this->getPostData(); + $id = $postData['ID'] ?? ''; + $fromVersion = (int) $postData['fromVersion'] ?? 0; + $fromStage = ucfirst(strtolower($postData['fromStage'])) ?? ''; + if ($fromStage === 'Draft') { + $fromStage = 'Stage'; + } + $toStage = ucfirst(strtolower($postData['toStage'])) ?? ''; + if ($toStage === 'Draft') { + $toStage = 'Stage'; + } + if (!in_array($fromStage, ['', Versioned::DRAFT, Versioned::LIVE]) + || !in_array($toStage, [Versioned::DRAFT, Versioned::LIVE]) + ) { + return $this->jsonResponse(400, null, 'Invalid request'); + } + $dataClass = BaseElement::class; + $record = null; + $from = null; + // todo: elemental revert will probably only ever use one of these, so remove the other one + if ($fromVersion) { + $record = Versioned::get_version($dataClass, $id, $fromVersion); + $from = $fromVersion; + } elseif ($fromStage) { + $record = Versioned::get_by_stage($dataClass, $fromVersion)->byID($id); + $from = $fromStage; + } else { + return $this->jsonResponse(400, null, 'You must provide either a fromStage or fromVersion argument'); + } + if (!$record) { + return $this->jsonResponse(400, null, "Record $id not found"); + } + $can = $toStage === Versioned::LIVE ? $record->canPublish(): $record->canEdit(); + if (!$can) { + return $this->jsonResponse(403, null, "Copying element from $from to $toStage is not allowed"); + } + /** @var DataObject|Versioned $record */ + $record->copyVersionToStage($from, $toStage); + return $this->jsonResponse(201); + } + + public function apiArchive(): HTTPResponse + { + $id = $this->getPostData()['ID'] ?? ''; + $element = BaseElement::get()->byID($id); + if (!$element) { + return $this->jsonResponse(400, null, "Element with ID $id does not exist"); + } + if (!$element->canDelete()) { + return $this->jsonResponse(403, null, "Unable to delete element with ID $id"); + } + $element->doArchive(); + return $this->jsonResponse(204); + } + + public function apiPublish(): HTTPResponse + { + $id = $this->getPostData()['ID'] ?? ''; + $element = BaseElement::get()->byID($id); + if (!$element) { + return $this->jsonResponse(400, null, "Element with ID $id does not exist"); + } + if (!$element->canPublish()) { + return $this->jsonResponse(403, null, "Unable to publish element with ID $id"); + } + $element->publishRecursive(); + return $this->jsonResponse(204); + } + + public function apiUnpublish(): HTTPResponse + { + $id = $this->getPostData()['ID'] ?? ''; + $element = BaseElement::get()->byID($id); + if (!$element) { + return $this->jsonResponse(400, null, "Element with ID $id does not exist"); + } + if (!$element->canUnpublish()) { + return $this->jsonResponse(403, null, "Unable to publish element with ID $id"); + } + $element->doUnpublish(); + return $this->jsonResponse(204); + } + + // Resolver.php resolveDuplicateBlock() + public function apiDuplicate(): HTTPResponse + { + $id = $this->getPostData()['ID'] ?? ''; + $element = BaseElement::get()->byID($id); + if (!$element) { + return $this->jsonResponse(400, null, "Element with ID $id does not exist"); + } + // check can edit the elemental area + $areaID = $element->ParentID; + $area = ElementalArea::get()->byID($areaID); + if (!$area) { + return $this->jsonResponse(400, null, "Invalid ParentID on BaseElement $id"); + } + if (!$area->canEdit()) { + return $this->jsonResponse(403, null, "Unable to edit element with ID $id"); + } + try { + // clone element + $clone = $element->duplicate(false); + $clone->Title = $this->getNewTitle($clone->Title ?? ''); + $clone->Sort = 0; // must be zeroed for reorder to work + $area->Elements()->add($clone); + // reorder + $reorderer = Injector::inst()->create(ReorderElements::class, $clone); + $reorderer->reorder($id); + return $this->jsonResponse(204); + } catch (Exception $e) { + return $this->jsonResponse(500, null, "Something went wrong when duplicating element with ID $id"); + } + } + + // Resolver.php newTitle() + private function getNewTitle(string $title = ''): ?string + { + $hasCopyPattern = '/^.*(\scopy($|\s[0-9]+$))/'; + $hasNumPattern = '/^.*(\s[0-9]+$)/'; + $parts = []; + // does $title end with 'copy' (ignoring numbers for now)? + if (preg_match($hasCopyPattern ?? '', $title ?? '', $parts)) { + $copy = $parts[1]; + // does $title end with numbers? + if (preg_match($hasNumPattern ?? '', $copy ?? '', $parts)) { + $num = trim($parts[1] ?? ''); + $len = strlen($num ?? ''); + $inc = (int)$num + 1; + return substr($title ?? '', 0, -$len) . "$inc"; + } else { + return $title . ' 2'; + } + } else { + return $title . ' copy'; + } + } + + // Modified from Resolver.php resolveSortBlock() + public function apiSort(): HTTPResponse + { + $postData = $this->getPostData(); + $id = $postData['ID'] ?? 0; + $afterBlockID = $postData['afterBlockID'] ?? 0; + $element = BaseElement::get()->byID($id); + if (!$element) { + return $this->jsonResponse(404); + } + if (!$element->canEdit()) { + return $this->jsonResponse(403); + } + $reorderingService = Injector::inst()->create(ReorderElements::class, $element); + $reorderingService->reorder($afterBlockID); + return $this->jsonResponse(204); + } + + public function apiReadElements(): HTTPResponse + { + $request = $this->getRequest(); + $elementalAreaID = $request->param('elementalAreaID'); + $elementalArea = ElementalArea::get()->byID($elementalAreaID); + if (!$elementalArea) { + return $this->jsonResponse(404); + } + if (!$elementalArea->canView()) { + return $this->jsonResponse(403); + } + $data = []; + foreach ($elementalArea->Elements() as $element) { + if (!$element->canView()) { + continue; + } + $typeName = str_replace('\\', '_', get_class($element)); // todo obsolete class name + // should probably be able to just red rid of this + $blockSchema = [ + 'typeName' => $typeName, + 'actions' => [ + 'edit' => Controller::join_links( + Director::absoluteBaseURL(), + "/admin/pages/edit/show/4" // todo pageID + ) + ], + 'content' => '', + ]; + $data[] = [ + 'id' => (string) $element->ID, + 'title' => $element->Title, + '__typename' => 'Block', // todo (delete) + 'blockSchema' => $blockSchema, + 'obsoleteClassName' => $element->getObsoleteClassName(), + 'version' => $element->Version, + 'isPublished' => $element->isPublished(), + 'isLiveVersion' => $element->isLiveVersion(), + 'canDelete' => $element->canDelete(), + 'canPublish' => $element->canPublish(), + 'canUnpublish' => $element->canUnpublish(), + 'canCreate' => $element->canCreate(), // todo shouldn't be in response? + ]; + } + return $this->jsonResponse(200, $data); + } + + // Resolver.php resolveAddElementToArea() + public function apiAdd(): HTTPResponse + { + $request = $this->getRequest(); + $postData = json_decode($request->getBody(), true); + $elementClass = $postData['elementClass']; + $elementalAreaID = $postData['elementalAreaID']; + $afterElementID = $postData['insertAfterElementID'] ?? null; + // validate post vars + if (!is_subclass_of($elementClass, BaseElement::class)) { + throw new InvalidArgumentException("$elementClass is not a subclass of " . BaseElement::class); + } + $elementalArea = ElementalArea::get()->byID($elementalAreaID); + if (!$elementalArea) { + throw new InvalidArgumentException("Invalid ElementalAreaID: $elementalAreaID"); + } + // permission checks + if (!$elementalArea->canEdit()) { + throw new InvalidArgumentException("The current user has insufficient permission to edit ElementalAreas"); + } + /** @var BaseElement $newElement */ + $newElement = Injector::inst()->create($elementClass); + if (!$newElement->canEdit()) { + throw new InvalidArgumentException( + 'The current user has insufficient permission to edit Elements' + ); + } + // Assign the parent ID directly rather than via HasManyList to prevent multiple writes. + // See BaseElement::$has_one for the "Parent" naming. + $newElement->ParentID = $elementalArea->ID; + // Ensure that a sort order is assigned - see BaseElement::ensureSortSet() + $newElement->ensureSortSet(); + if ($afterElementID !== null) { + /** @var ReorderElements $reorderer */ + $reorderer = Injector::inst()->create(ReorderElements::class, $newElement); + $reorderer->reorder($afterElementID); // also writes the element + } else { + $newElement->write(); + } + $response = $this->getResponse(); + $response->setStatusCode(201); + return $response; + } + + // === + public function getClientConfig() { $clientConfig = parent::getClientConfig(); @@ -46,6 +346,8 @@ public function getClientConfig() 'schemaUrl' => $this->Link('schema/elementForm'), 'formNameTemplate' => sprintf(static::FORM_NAME_TEMPLATE, '{id}'), ]; + $clientConfig['controllerLink'] = $this->Link(); + // Configuration that is available per element type $clientConfig['elementTypes'] = ElementTypeRegistry::generate()->getDefinitions(); return $clientConfig; @@ -60,7 +362,6 @@ public function getClientConfig() public function elementForm(): Form { $id = $this->getRequest()->param('ItemID'); - // Note that new elements are added via graphql, so only using this endpoint for editing existing $element = BaseElement::get()->byID($id); if (!$element) { $this->jsonError(404); diff --git a/src/GraphQL/Resolvers/Resolver.php b/src/GraphQL/Resolvers/Resolver.php deleted file mode 100644 index 2d1315e7..00000000 --- a/src/GraphQL/Resolvers/Resolver.php +++ /dev/null @@ -1,197 +0,0 @@ -value; - } - - /** - * @param $obj - * @param array $args - * @param array $context - * @param ResolveInfo $info - * @return BaseElement - * @throws ValidationException - * @throws InvalidArgumentException - */ - public static function resolveAddElementToArea( - $obj, - array $args, - array $context, - ResolveInfo $info - ): BaseElement { - $elementClass = $args['className']; - $elementalAreaID = $args['elementalAreaID']; - $afterElementID = $args['afterElementID'] ?? null; - - if (!is_subclass_of($elementClass, BaseElement::class)) { - throw new InvalidArgumentException("$elementClass is not a subclass of " . BaseElement::class); - } - - $elementalArea = ElementalArea::get()->byID($elementalAreaID); - - if (!$elementalArea) { - throw new InvalidArgumentException("Invalid ElementalAreaID: $elementalAreaID"); - } - - $member = UserContextProvider::get($context); - if (!$elementalArea->canEdit($member)) { - throw new InvalidArgumentException("The current user has insufficient permission to edit ElementalAreas"); - } - - /** @var BaseElement $newElement */ - $newElement = Injector::inst()->create($elementClass); - - $member = UserContextProvider::get($context); - if (!$newElement->canEdit($member)) { - throw new InvalidArgumentException( - 'The current user has insufficient permission to edit Elements' - ); - } - - // Assign the parent ID directly rather than via HasManyList to prevent multiple writes. - // See BaseElement::$has_one for the "Parent" naming. - $newElement->ParentID = $elementalArea->ID; - // Ensure that a sort order is assigned - $newElement->ensureSortSet(); - - if ($afterElementID !== null) { - /** @var ReorderElements $reorderer */ - $reorderer = Injector::inst()->create(ReorderElements::class, $newElement, true); - $reorderer->reorder($afterElementID); // also writes the element - } else { - $newElement->write(skipValidation: true); - } - - return $newElement; - } - - /** - * @param $object - * @param array $args - * @param $context - * @param ResolveInfo $info - * @return BaseElement - * @throws ValidationException - */ - public static function resolveDuplicateBlock($object, array $args, $context, ResolveInfo $info) - { - // load element to clone - $elementID = $args['id']; - $element = BaseElement::get_by_id($elementID); - if (!$element) { - throw new InvalidArgumentException("Invalid BaseElementID: $elementID"); - } - - // check can edit the elemental area - $areaID = $element->ParentID; - $area = ElementalArea::get_by_id($areaID); - if (!$area) { - throw new InvalidArgumentException("Invalid ParentID on BaseElement: $elementID"); - } - $member = UserContextProvider::get($context); - if (!$area->canEdit($member)) { - throw new InvalidArgumentException( - "The current user has insufficient permission to edit ElementalArea: $areaID" - ); - } - - try { - // clone element - $clone = $element->duplicate(false); - $clone->Title = static::newTitle($clone->Title ?? ''); - $clone->Sort = 0; // must be zeroed for reorder to work - $area->Elements()->add($clone); - - // reorder - $reorderer = Injector::inst()->create(ReorderElements::class, $clone); - $reorderer->reorder($elementID); - - return $clone; - } catch (Exception $e) { - throw new Exception("Something went wrong when duplicating element: $elementID"); - } - } - - public static function newTitle(string $title = ''): ?string - { - $hasCopyPattern = '/^.*(\scopy($|\s[0-9]+$))/'; - $hasNumPattern = '/^.*(\s[0-9]+$)/'; - $parts = []; - - // does $title end with 'copy' (ignoring numbers for now)? - if (preg_match($hasCopyPattern ?? '', $title ?? '', $parts)) { - $copy = $parts[1]; - // does $title end with numbers? - if (preg_match($hasNumPattern ?? '', $copy ?? '', $parts)) { - $num = trim($parts[1] ?? ''); - $len = strlen($num ?? ''); - $inc = (int)$num + 1; - return substr($title ?? '', 0, -$len) . "$inc"; - } else { - return $title . ' 2'; - } - } else { - return $title . ' copy'; - } - } - - public static function resolveSortBlock($object, array $args, $context, ResolveInfo $info) - { - $element = BaseElement::get()->byID($args['id']); - - if (!$element) { - throw new InvalidArgumentException(sprintf( - '%s#%s not found', - BaseElement::class, - $args['ID'] - )); - } - $member = UserContextProvider::get($context); - if (!$element->canEdit($member)) { - throw new InvalidArgumentException( - 'Changing the sort order of blocks is not allowed for the current user' - ); - } - - $reorderingService = Injector::inst()->create(ReorderElements::class, $element); - return $reorderingService->reorder($args['afterBlockID']); - } -} diff --git a/src/Models/BaseElement.php b/src/Models/BaseElement.php index 16513727..d4168058 100644 --- a/src/Models/BaseElement.php +++ b/src/Models/BaseElement.php @@ -4,7 +4,6 @@ use DNADesign\Elemental\Controllers\ElementController; use DNADesign\Elemental\Forms\TextCheckboxGroupField; -use DNADesign\Elemental\ORM\FieldType\DBObjectType; use DNADesign\Elemental\TopPage\DataExtension; use Exception; use SilverStripe\CMS\Controllers\CMSPageEditController; @@ -18,8 +17,6 @@ use SilverStripe\Forms\HiddenField; use SilverStripe\Forms\NumericField; use SilverStripe\Forms\TextField; -use SilverStripe\GraphQL\Scaffolding\StaticSchema; -use SilverStripe\GraphQL\Schema\Exception\SchemaBuilderException; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBBoolean; use SilverStripe\ORM\FieldType\DBField; @@ -34,7 +31,6 @@ use SilverStripe\View\Requirements; use SilverStripe\ORM\CMSPreviewable; use SilverStripe\Core\Config\Config; -use SilverStripe\ORM\DataObjectSchema; use SilverStripe\ORM\ValidationResult; /** @@ -94,7 +90,6 @@ class BaseElement extends DataObject implements CMSPreviewable ]; private static $casting = [ - 'BlockSchema' => DBObjectType::class, 'IsLiveVersion' => DBBoolean::class, 'IsPublished' => DBBoolean::class, 'canCreate' => DBBoolean::class, @@ -1049,7 +1044,7 @@ public function getMimeType() /** * This can be overridden on child elements to create a summary for display in GridFields. * The react Summary component takes `content` (html) and/or `fileUrl` & `fileTitle` (image) props, - * which have to be added to the graphql output in `provideBlockSchema()`. + * which have to be added to the output in `provideBlockSchema()`. * * @return string */ @@ -1069,48 +1064,7 @@ public static function getBlockConfig() return []; } - /** - * The block actions is an associative array available for providing data to the client side to be used to describe - * actions that may be performed. This is available as a plain "ObjectType" in the GraphQL schema. - * - * By default the only action is "edit" which is simply the URL where the block may be edited. - * - * To modify the actions, either use the extension point or overload the `provideBlockSchema` method. - * - * @internal This API may change in future. Treat this as a `final` method. - * @return array - */ - public function getBlockSchema() - { - $blockSchema = $this->provideBlockSchema(); - - $this->extend('updateBlockSchema', $blockSchema); - - return $blockSchema; - } - - /** - * Provide block schema data, which will be serialised and sent via GraphQL to the editor client. - * - * Overload this method in child element classes to augment, or use the extension point on `getBlockSchema` - * to update it from an `Extension`. - * - * @return array - * @throws SchemaBuilderException - * @throws ValidationException - */ - protected function provideBlockSchema() - { - return [ - 'typeName' => static::getGraphQLTypeName(), - 'actions' => [ - 'edit' => $this->getEditLink(), - ], - 'content' => $this->getSummary(), - ]; - } - - /** + /** * Generate markup for element type icons suitable for use in GridFields. * * @return null|DBHTMLText @@ -1274,12 +1228,8 @@ public function EvenOdd() /** * @return string */ - public static function getGraphQLTypeName(): string + public static function getTypeName(): string { - // For GraphQL 3, use the static schema type name - except for BaseElement for which this is inconsistent. - if (class_exists(StaticSchema::class) && static::class !== BaseElement::class) { - return StaticSchema::inst()->typeNameForDataObject(static::class); - } return str_replace('\\', '_', static::class); } } diff --git a/src/ORM/FieldType/DBObjectType.php b/src/ORM/FieldType/DBObjectType.php deleted file mode 100644 index f91710ed..00000000 --- a/src/ORM/FieldType/DBObjectType.php +++ /dev/null @@ -1,21 +0,0 @@ -getType('ObjectType'); - } -} diff --git a/src/Services/ElementTypeRegistry.php b/src/Services/ElementTypeRegistry.php index 61be6e3a..dec1587e 100644 --- a/src/Services/ElementTypeRegistry.php +++ b/src/Services/ElementTypeRegistry.php @@ -48,11 +48,10 @@ public function registerElement($elementClass) } // Get the GraphQL type name - $typeName = $singleton->getGraphQLTypeName(); $this->elementTypes[] = [ 'icon' => $singleton::config()->get('icon'), - 'name' => $typeName, + 'name' => $singleton->getTypeName(), 'class' => $elementClass, 'title' => $singleton->getType(), 'inlineEditable' => $singleton->inlineEditable(), @@ -70,7 +69,7 @@ private function registerBrokenElement() $singleton = singleton(BaseElement::class); $this->elementTypes[] = [ 'icon' => 'font-icon-block', - 'name' => $singleton->getGraphQLTypeName(), + 'name' => $singleton->getTypeName(), 'class' => BaseElement::class, 'title' => '', 'inlineEditable' => false, diff --git a/tests/GraphQL/AddElementToAreaMutationTest.php b/tests/GraphQL/AddElementToAreaMutationTest.php deleted file mode 100644 index 5504074d..00000000 --- a/tests/GraphQL/AddElementToAreaMutationTest.php +++ /dev/null @@ -1,95 +0,0 @@ -markTestSkipped('Skipped GraphQL 4 test ' . __CLASS__); - } - } - - public function testAddingBlocksInOrder() - { - $className = TestElement::class; - $areaID = $this->objFromFixture(ElementalArea::class, 'one')->ID; - - $one = $this->runMutation($className, $areaID)->ID; - $this->assertIdsInOrder([$one], 'The first element is added'); - - $two = $this->runMutation($className, $areaID, $one)->ID; - $this->assertIdsInOrder([$one, $two], 'The second element is added after the first'); - - $three = $this->runMutation($className, $areaID, $one)->ID; - $this->assertIdsInOrder([$one, $three, $two], 'The third element is added after the first'); - - $four = $this->runMutation($className, $areaID)->ID; - $this->assertIdsInOrder( - [$one, $three, $two, $four], - 'The fourth element is added last, when no after ID parameter is given' - ); - - $five = $this->runMutation($className, $areaID, 0)->ID; - $this->assertIdsInOrder([$five, $one, $three, $two, $four], 'The fifth element is added first, after ID 0'); - } - - public function testBadElementalArea() - { - $this->expectException(InvalidArgumentException::class); - $areaID = $this->objFromFixture(ElementalArea::class, 'one')->ID; - $this->runMutation(TestElement::class, $areaID + 1); - } - - public function testOrderingByWrongElementalArea() - { - $this->expectException(InvalidArgumentException::class); - $firstArea = ElementalArea::get()->first(); - $elementInFirstArea = TestElement::create(); - $firstArea->Elements()->add($elementInFirstArea); - - ElementalArea::create()->write(); - $this->runMutation(TestElement::class, 2, $elementInFirstArea->ID); - } - - protected function assertIdsInOrder($ids, $message = null) - { - $actualIDs = TestElement::get()->sort('Sort')->map()->keys(); - // Ideally this should be assertSame, but sometimes the database - // returns IDs as strings instead of integers (e.g. "1" instead of 1). - $this->assertEquals($ids, $actualIDs, $message); - } - - protected function runMutation($className, $elementalAreaID, $afterElementId = null) - { - $context = ['currentUser' => Security::getCurrentUser()]; - $resolveInfo = new FakeResolveInfo(); - - $args = [ - 'className' => $className, - 'elementalAreaID' => $elementalAreaID, - ]; - - if (!is_null($afterElementId)) { - $args['afterElementID'] = $afterElementId; - } - - return Resolver::resolveAddElementToArea(null, $args, $context, $resolveInfo); - } -} diff --git a/tests/GraphQL/AddElementToAreaMutationTest.yml b/tests/GraphQL/AddElementToAreaMutationTest.yml deleted file mode 100644 index 4f099556..00000000 --- a/tests/GraphQL/AddElementToAreaMutationTest.yml +++ /dev/null @@ -1,2 +0,0 @@ -DNADesign\Elemental\Models\ElementalArea: - one: \ No newline at end of file diff --git a/tests/GraphQL/DuplicateElementMutationTest.php b/tests/GraphQL/DuplicateElementMutationTest.php deleted file mode 100644 index 66a94b2d..00000000 --- a/tests/GraphQL/DuplicateElementMutationTest.php +++ /dev/null @@ -1,72 +0,0 @@ -markTestSkipped('Skipped GraphQL 4 test ' . __CLASS__); - } - } - - public function testResolvePermissions() - { - $cannotEditClass = new class extends BaseElement { - public function canEdit($member = null) - { - return false; - } - public function canCreate($member = null, $context = []) - { - return true; - } - }; - - $cannotCreateClass = new class extends BaseElement { - public function canEdit($member = null) - { - return true; - } - public function canCreate($member = null, $context = []) - { - return false; - } - }; - - $area = new ElementalArea(); - $area->write(); - - $testCases = [ - [$cannotEditClass, 'edit'], - [$cannotCreateClass, 'create'], - ]; - - foreach ($testCases as $data) { - [$class, $operation] = $data; - - $element = new $class(); - $element->ParentID = $area->ID; - $element->write(); - $mutation = new DuplicateElementMutation(); - $object = null; - $args = ['id' => $element->ID]; - $context = ['currentUser' => Security::getCurrentUser()]; - $resolveInfo = new FakeResolveInfo(); - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageRegExp("#insufficient permission to {$operation}#"); - $mutation->resolve($object, $args, $context, $resolveInfo); - } - } -} diff --git a/tests/GraphQL/FakeResolveInfo.php b/tests/GraphQL/FakeResolveInfo.php deleted file mode 100644 index 8618ace6..00000000 --- a/tests/GraphQL/FakeResolveInfo.php +++ /dev/null @@ -1,28 +0,0 @@ - 'fake', 'type' => Type::string()]), - new \ArrayObject(), - new ObjectType(['name' => 'fake']), - [], - new Schema([]), - [], - '', - new OperationDefinitionNode([]), - [] - ); - } -} diff --git a/tests/GraphQL/SortBlockMutationCreatorTest.php b/tests/GraphQL/SortBlockMutationCreatorTest.php deleted file mode 100644 index 8c50499c..00000000 --- a/tests/GraphQL/SortBlockMutationCreatorTest.php +++ /dev/null @@ -1,64 +0,0 @@ -markTestSkipped('Skipped GraphQL 4 test ' . __CLASS__); - } - } - - /** - * Reorders blocks by compounding each result into the next test (rather than isolated sorting tests) - */ - public function testSortBlock() - { - $this->runMutation(1, 3); - $this->assertIdsInOrder([2, 3, 1, 4]); - - $this->runMutation(4, 2); - $this->assertIdsInOrder([2, 4, 3, 1]); - - $this->runMutation(1, 0); - $this->assertIdsInOrder([1, 2, 4, 3]); - - $this->runMutation(3, 2); - $this->assertIdsInOrder([1, 2, 3, 4]); - } - - protected function assertIdsInOrder($ids) - { - $actualIDs = TestElement::get()->sort('Sort')->map()->keys(); - - $this->assertSame($ids, $actualIDs); - } - - protected function runMutation($id, $afterBlockId) - { - $member = Security::getCurrentUser(); - - $context = ['currentUser' => $member]; - $resolveInfo = new FakeResolveInfo(); - - Resolver::resolveSortBlock(null, [ - 'id' => $id, - 'afterBlockID' => $afterBlockId, - ], $context, $resolveInfo); - } -} diff --git a/tests/GraphQL/SortBlockMutationCreatorTest.yml b/tests/GraphQL/SortBlockMutationCreatorTest.yml deleted file mode 100644 index d54cb617..00000000 --- a/tests/GraphQL/SortBlockMutationCreatorTest.yml +++ /dev/null @@ -1,13 +0,0 @@ -DNADesign\Elemental\Tests\Src\TestElement: - element1: - ID: 1 - Sort: 1 - element2: - ID: 2 - Sort: 2 - element3: - ID: 3 - Sort: 3 - element4: - ID: 4 - Sort: 4 diff --git a/tests/Reports/ElementsInUseReportTest.php b/tests/Reports/ElementsInUseReportTest.php index 2f22432e..4b8871ff 100644 --- a/tests/Reports/ElementsInUseReportTest.php +++ b/tests/Reports/ElementsInUseReportTest.php @@ -9,7 +9,6 @@ use DNADesign\Elemental\Tests\Src\TestElement; use DNADesign\Elemental\Tests\Src\TestPage; use SilverStripe\Dev\FunctionalTest; -use SilverStripe\GraphQL\Tests\Schema\NaiveSchemaBuilder; use SilverStripe\ORM\DataList; class ElementsInUseReportTest extends FunctionalTest @@ -27,26 +26,6 @@ class ElementsInUseReportTest extends FunctionalTest TestPage::class, ]; - protected function setUp(): void - { - parent::setUp(); - - // GraphQL 4 only - if (class_exists(NaiveSchemaBuilder::class)) { - NaiveSchemaBuilder::activate(); - } - } - - protected function tearDown(): void - { - parent::tearDown(); - - // GraphQL 4 only - if (class_exists(NaiveSchemaBuilder::class)) { - NaiveSchemaBuilder::deactivate(); - } - } - public function testReportShowsElementsInUse() { $this->logInWithPermission('ADMIN'); diff --git a/yarn.lock b/yarn.lock index 79978c28..262315fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,26 +15,6 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@apollo/client@^3.7.1": - version "3.9.9" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.9.9.tgz#38f983a1ad24e2687abfced0a9c1c3bef8d32616" - integrity sha512-/sMecU/M0WK9knrguts1lSLV8xFKzIgOMVb4mi6MOxgJXjliDB8PvOtmXhTqh2cVMMR4TzXgOnb+af/690zlQw== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - "@wry/caches" "^1.0.0" - "@wry/equality" "^0.5.6" - "@wry/trie" "^0.5.0" - graphql-tag "^2.12.6" - hoist-non-react-statics "^3.3.2" - optimism "^0.18.0" - prop-types "^15.7.2" - rehackt "0.0.6" - response-iterator "^0.2.6" - symbol-observable "^4.0.0" - ts-invariant "^0.10.3" - tslib "^2.3.0" - zen-observable-ts "^1.2.5" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2": version "7.24.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" @@ -1110,11 +1090,6 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@graphql-typed-document-node/core@^3.1.1": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" - integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== - "@humanwhocodes/config-array@^0.11.14": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -2007,41 +1982,6 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== -"@wry/caches@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" - integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== - dependencies: - tslib "^2.3.0" - -"@wry/context@^0.7.0": - version "0.7.4" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" - integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== - dependencies: - tslib "^2.3.0" - -"@wry/equality@^0.5.6": - version "0.5.7" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" - integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== - dependencies: - tslib "^2.3.0" - -"@wry/trie@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" - integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== - dependencies: - tslib "^2.3.0" - -"@wry/trie@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" - integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== - dependencies: - tslib "^2.3.0" - "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -2646,7 +2586,7 @@ cacache@^16.0.0, cacache@^16.1.0, cacache@^16.1.3: tar "^6.1.11" unique-filename "^2.0.0" -call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== @@ -2672,6 +2612,11 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +caniuse-lite@^1.0.30001565: + version "1.0.30001642" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz#6aa6610eb24067c246d30c57f055a9d0a7f8d05f" + integrity sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA== + caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: version "1.0.30001603" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001603.tgz#605046a5bdc95ba4a92496d67e062522dce43381" @@ -3300,6 +3245,11 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +electron-to-chromium@^1.4.601: + version "1.4.828" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.828.tgz#a1ee8cd8847448b2898d3f2d9db02113f9c5b35c" + integrity sha512-QOIJiWpQJDHAVO4P58pwb133Cwee0nbvy/MV1CwzZVGpkH1RX33N3vsaWRCpR6bF63AAq366neZrRTu7Qlsbbw== + electron-to-chromium@^1.4.668: version "1.4.722" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.722.tgz#99ae3484c5fc0f387d39ad98d77e1f259b9f4074" @@ -4045,7 +3995,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== @@ -4186,18 +4136,6 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -graphql-tag@^2.12.6: - version "2.12.6" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" - integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== - dependencies: - tslib "^2.1.0" - -graphql@^16.8.1: - version "16.8.1" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" - integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== - gud@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" @@ -6163,6 +6101,11 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.9.0: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + object-is@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" @@ -6251,16 +6194,6 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -optimism@^0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63" - integrity sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ== - dependencies: - "@wry/caches" "^1.0.0" - "@wry/context" "^0.7.0" - "@wry/trie" "^0.4.3" - tslib "^2.3.0" - optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -6636,7 +6569,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -6957,11 +6890,6 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" -rehackt@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.0.6.tgz#7a0a2247f2295e7548915417e44fbbf03bf004f4" - integrity sha512-l3WEzkt4ntlEc/IB3/mF6SRgNHA6zfQR7BlGOgBTOmx7IJJXojDASav+NsgXHFjHn+6RmwqsGPFgZpabWpeOdw== - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -7043,11 +6971,6 @@ resolve@^2.0.0-next.5: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -response-iterator@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da" - integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== - retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -7174,7 +7097,7 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-function-length@^1.1.1: +set-function-length@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== @@ -7625,11 +7548,6 @@ symbol-observable@^1.0.4: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -symbol-observable@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" - integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== - symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -7747,13 +7665,6 @@ treeverse@^2.0.0: resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" integrity sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A== -ts-invariant@^0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" - integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== - dependencies: - tslib "^2.1.0" - tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -7764,11 +7675,6 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.1.0, tslib@^2.3.0: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -8334,15 +8240,3 @@ yocto-queue@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== - -zen-observable-ts@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" - integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== - dependencies: - zen-observable "0.8.15" - -zen-observable@0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" - integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==