diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 4e0ce7f1..b5abfbc1 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1 +1 @@ -!function(){"use strict";var e={274:function(e,t,n){var r,l=(r=n(521))&&r.__esModule?r:{default:r};document.addEventListener("DOMContentLoaded",(()=>{(0,l.default)()}))},521:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(648)),l=u(n(809)),o=u(n(852)),i=u(n(117)),a=u(n(606));function u(e){return e&&e.__esModule?e:{default:e}}var s=()=>{r.default.component.registerMany({LinkPicker:l.default,LinkField:o.default,"LinkModal.FormBuilderModal":i.default,"LinkModal.InsertMediaModal":a.default})};t.default=s},852:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=_(n(363)),l=n(827),o=n(624),i=(n(648),m(n(42))),a=m(n(809)),u=m(n(734)),s=m(n(686)),d=m(n(697)),f=_(n(123)),c=m(n(159)),p=m(n(510)),y=m(n(86)),v=m(n(754));function m(e){return e&&e.__esModule?e:{default:e}}function k(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(k=function(e){return e?n:t})(e)}function _(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=k(t);if(n&&n.has(e))return n.get(e);var r={},l=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var i=l?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(r,o,i):r[o]=e[o]}return r.default=e,n&&n.set(e,r),r}const h="SilverStripe\\LinkField\\Controllers\\LinkFieldController",O=e=>{var t;let{value:n=null,onChange:l,types:o=[],actions:i,isMulti:s=!1}=e;const[f,y]=(0,r.useState)({}),[m,k]=(0,r.useState)(0);let _=n;Array.isArray(_)||("number"==typeof _&&0!=_&&(_=[_]),_||(_=[])),(0,r.useEffect)((()=>{if(!m&&_.length>0){const e=[];for(const t of _)e.push(`itemIDs[]=${t}`);const t=`${p.default.getSection(h).form.linkForm.dataUrl}?${e.join("&")}`;c.default.get(t).then((e=>e.json())).then((e=>{y(e)}))}}),[m,n&&n.length]);const O=()=>{k(0)},g=e=>{k(0);const t=[..._];t.includes(e)||t.push(e),l(s?t:t[0]),i.toasts.success(v.default._t("LinkField.SAVE_SUCCESS","Saved link"))},b=e=>{const t=`${p.default.getSection(h).form.linkForm.deleteUrl}/${e}`;c.default.delete(t,{},{"X-SecurityID":p.default.get("SecurityID")}).then((()=>{i.toasts.success(v.default._t("LinkField.DELETE_SUCCESS","Deleted link"))})).catch((()=>{i.toasts.error(v.default._t("LinkField.DELETE_ERROR","Failed to delete link"))}));const n={...f};delete n[e],y(n),l(s?Object.keys(n):0)},M=s||0===Object.keys(f).length,j=Boolean(m);return r.default.createElement(r.default.Fragment,null,M&&r.default.createElement(a.default,{onModalSuccess:g,onModalClosed:O,types:o}),r.default.createElement("div",null," ",(()=>{const e=[];for(const s of _){var t,n,l,i,a;if(!f[s])continue;const d=o.hasOwnProperty(null===(t=f[s])||void 0===t?void 0:t.typeKey)?o[null===(n=f[s])||void 0===n?void 0:n.typeKey]:{};e.push(r.default.createElement(u.default,{key:s,id:s,title:null===(l=f[s])||void 0===l?void 0:l.Title,description:null===(i=f[s])||void 0===i?void 0:i.description,versionState:null===(a=f[s])||void 0===a?void 0:a.versionState,typeTitle:d.title||"",onClear:b,onClick:()=>{k(s)}}))}return e})()," "),j&&r.default.createElement(d.default,{types:o,typeKey:null===(t=f[m])||void 0===t?void 0:t.typeKey,isOpen:Boolean(m),onSuccess:g,onClosed:O,linkID:m}))};O.propTypes={value:y.default.oneOfType([y.default.arrayOf(y.default.number),y.default.number]),onChange:y.default.func.isRequired,types:y.default.objectOf(s.default).isRequired,actions:y.default.object.isRequired,isMulti:y.default.bool};var g=(0,l.compose)(i.default,(0,o.connect)(null,(e=>({actions:{toasts:(0,l.bindActionCreators)(f,e)}}))))(O);t.default=g},606:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;s(n(754));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={},l=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var i=l?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(r,o,i):r[o]=e[o]}r.default=e,n&&n.set(e,r);return r}(n(363)),l=s(n(475)),o=n(624),i=s(n(686)),a=s(n(86));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)}function s(e){return e&&e.__esModule?e:{default:e}}function d(){return d=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let{type:t,editing:n,data:o,actions:i,onSubmit:a,...u}=e;if(!t)return!1;(0,r.useEffect)((()=>{n?i.initModal():i.reset()}),[n]);const s=o?{ID:o.FileID,Description:o.Title,TargetBlank:!!o.OpenInNew}:{};return r.default.createElement(l.default,d({isOpen:n,type:"insert-link",title:!1,bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:s,onInsert:e=>{let{ID:n,Description:r,TargetBlank:l}=e;return a({FileID:n,Title:r,OpenInNew:l,typeKey:t.key},"",(()=>{}))}},u))};f.propTypes={type:i.default.isRequired,editing:a.default.bool.isRequired,data:a.default.object.isRequired,actions:a.default.object.isRequired,onClick:a.default.func.isRequired};var c=(0,o.connect)((function(){return{}}),(function(e){return{actions:{initModal:()=>e({type:"INIT_FORM_SCHEMA_STACK",payload:{formSchema:{type:"insert-link",nextType:"admin"}}}),reset:()=>e({type:"RESET"})}}}))(f);t.default=c},117:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=s(n(363)),l=s(n(912)),o=s(n(872)),i=s(n(902)),a=s(n(510)),u=s(n(86));function s(e){return e&&e.__esModule?e:{default:e}}const d=(e,t)=>{const{schemaUrl:n}=a.default.getSection("SilverStripe\\LinkField\\Controllers\\LinkFieldController").form.linkForm,r=o.default.parse(n),l=i.default.parse(r.query);l.typeKey=e;for(const e of["href","path","pathname"])r[e]=`${r[e]}/${t}`;return o.default.format({...r,search:i.default.stringify(l)})},f=e=>{let{typeTitle:t,typeKey:n,linkID:o=0,isOpen:i,onSuccess:a,onClosed:u}=e;if(!n)return!1;return r.default.createElement(l.default,{title:t,isOpen:i,schemaUrl:d(n,o),identifier:"Link.EditingLinkInfo",onSubmit:async(e,t,n)=>{const r=await n();if(!r.id.match(/\/schema\/linkfield\/([0-9]+)/)){const e=r.id.match(/\/linkForm\/([0-9]+)/),t=parseInt(e[1],10);a(t)}return Promise.resolve()},onClosed:u})};f.propTypes={typeTitle:u.default.string.isRequired,typeKey:u.default.string.isRequired,linkID:u.default.number,isOpen:u.default.bool.isRequired,onSuccess:u.default.func.isRequired,onClosed:u.default.func.isRequired};var c=f;t.default=c},809: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={},l=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var i=l?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(r,o,i):r[o]=e[o]}r.default=e,n&&n.set(e,r);return r}(n(363)),l=s(n(86)),o=s(n(820)),i=s(n(97)),a=s(n(686)),u=s(n(697));function s(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 f=e=>{let{types:t,onModalSuccess:n,onModalClosed:l}=e;const[a,s]=(0,r.useState)(""),d=""!==a,f=(0,o.default)("link-picker","form-control"),c=Object.values(t);return r.default.createElement("div",{className:f},r.default.createElement(i.default,{types:c,onSelect:e=>{s(e)}}),d&&r.default.createElement(u.default,{types:t,typeKey:a,isOpen:d,onSuccess:e=>{s(""),n(e)},onClosed:()=>{"function"==typeof l&&l(),s("")}}))};t.Component=f,f.propTypes={types:l.default.objectOf(a.default).isRequired,onModalSuccess:l.default.func.isRequired,onModalClosed:l.default.func};var c=f;t.default=c},97:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=s(n(754)),l=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={},l=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var i=l?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(r,o,i):r[o]=e[o]}r.default=e,n&&n.set(e,r);return r}(n(363)),o=s(n(86)),i=n(127),a=s(n(686));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)}function s(e){return e&&e.__esModule?e:{default:e}}const d=e=>{let{types:t,onSelect:n}=e;const[o,a]=(0,l.useState)(!1);return l.default.createElement(i.Dropdown,{isOpen:o,toggle:()=>a((e=>!e)),className:"link-picker__menu"},l.default.createElement(i.DropdownToggle,{className:"link-picker__menu-toggle font-icon-plus-1",caret:!0},r.default._t("LinkField.ADD_LINK","Add Link")),l.default.createElement(i.DropdownMenu,null,t.map((e=>{let{key:t,title:r}=e;return l.default.createElement(i.DropdownItem,{key:t,onClick:()=>n(t)},r)}))))};d.propTypes={types:o.default.arrayOf(a.default).isRequired,onSelect:o.default.func.isRequired};var f=d;t.default=f},734:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(820)),l=u(n(754)),o=u(n(363)),i=u(n(86)),a=n(127);function u(e){return e&&e.__esModule?e:{default:e}}const s=e=>t=>{t.nativeEvent.stopImmediatePropagation(),t.preventDefault(),t.nativeEvent.preventDefault(),t.stopPropagation(),e&&e()},d=e=>{let{id:t,title:n,description:i,versionState:u,typeTitle:d,onClear:f,onClick:c}=e;const p={"link-picker__link":!0,"form-control":!0};u&&(p[` link-picker__link--${u}`]=!0),n&&n.length>25&&(n=n.substring(0,25)+"...");const y=(0,r.default)(p);return o.default.createElement("div",{className:y},o.default.createElement(a.Button,{className:"link-picker__button font-icon-link",color:"secondary",onClick:s(c)},o.default.createElement("div",{className:"link-picker__link-detail"},o.default.createElement("div",{className:"link-picker__title"},o.default.createElement("span",{className:"link-picker__title-text"},n),(e=>{let t="",n="";if("draft"===e)t=l.default._t("LinkField.LINK_DRAFT_TITLE","Link has draft changes"),n=l.default._t("LinkField.LINK_DRAFT_LABEL","Draft");else{if("modified"!==e)return null;t=l.default._t("LinkField.LINK_MODIFIED_TITLE","Link has unpublished changes"),n=l.default._t("LinkField.LINK_MODIFIED_LABEL","Modified")}const i=(0,r.default)("badge",`status-${e}`);return o.default.createElement("span",{className:i,title:t},n)})(u)),o.default.createElement("small",{className:"link-picker__type"},d,": ",o.default.createElement("span",{className:"link-picker__url"},i)))),o.default.createElement(a.Button,{className:"link-picker__clear",color:"link",onClick:s((()=>f(t)))},l.default._t("LinkField.CLEAR","Clear")))};d.propTypes={id:i.default.number.isRequired,title:i.default.string,description:i.default.string,versionState:i.default.string,typeTitle:i.default.string.isRequired,onClear:i.default.func.isRequired,onClick:i.default.func.isRequired};var f=d;t.default=f},697:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=a(n(363)),l=n(648),o=a(n(86)),i=a(n(686));function a(e){return e&&e.__esModule?e:{default:e}}const u=e=>{let{types:t,typeKey:n,linkID:o=0,isOpen:i,onSuccess:a,onClosed:u}=e;if(!n)return!1;const s=t.hasOwnProperty(n)?t[n]:{},d=s&&s.hasOwnProperty("handlerName")?s.handlerName:"FormBuilderModal",f=(0,l.loadComponent)(`LinkModal.${d}`);return r.default.createElement(f,{typeTitle:s.title||"",typeKey:n,linkID:o,isOpen:i,onSuccess:a,onClosed:u})};u.propTypes={types:o.default.objectOf(i.default).isRequired,typeKey:o.default.string.isRequired,linkID:o.default.number,isOpen:o.default.bool.isRequired,onSuccess:o.default.func.isRequired,onClosed:o.default.func.isRequired};var s=u;t.default=s},41:function(e,t,n){var r=a(n(311)),l=a(n(363)),o=a(n(691)),i=n(648);function a(e){return e&&e.__esModule?e:{default:e}}function u(){return u=Object.assign?Object.assign.bind():function(e){for(var t=1;t{e(".js-injector-boot .entwine-linkfield").entwine({Component:null,Root:null,onmatch(){const e=this.closest(".cms-content").attr("id"),t=e?{context:e}:{},n=this.data("schema-component"),r=(0,i.loadComponent)(n,t);this.setComponent(r),this.setRoot(o.default.createRoot(this[0])),this._super(),this.refresh()},refresh(){const e=this.getProps();this.getInputField().val(e.value);const t=this.getComponent();this.getRoot().render(l.default.createElement(t,u({},e,{noHolder:!0})))},handleChange(e){this.getInputField().data("value",e),this.refresh()},getProps(){return{value:this.getInputField().data("value"),onChange:this.handleChange.bind(this),isMulti:this.data("is-multi")??!1,types:this.data("types")??[]}},getInputField(){const t=this.data("field-id");return e(`#${t}`)},onunmatch(){const e=this.getRoot();e&&e.unmount()}})}))},686:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,l=(r=n(86))&&r.__esModule?r:{default:r};var o=l.default.shape({key:l.default.string.isRequired,title:l.default.string.isRequired});t.default=o},159:function(e){e.exports=Backend},510:function(e){e.exports=Config},42:function(e){e.exports=FieldHolder},912:function(e){e.exports=FormBuilderModal},648:function(e){e.exports=Injector},475:function(e){e.exports=InsertMediaModal},872:function(e){e.exports=NodeUrl},86:function(e){e.exports=PropTypes},363:function(e){e.exports=React},691:function(e){e.exports=ReactDomClient},624:function(e){e.exports=ReactRedux},127:function(e){e.exports=Reactstrap},827:function(e){e.exports=Redux},123:function(e){e.exports=ToastsActions},820:function(e){e.exports=classnames},754:function(e){e.exports=i18n},311:function(e){e.exports=jQuery},902:function(e){e.exports=qs}},t={};function n(r){var l=t[r];if(void 0!==l)return l.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,n),o.exports}n(274),n(41)}(); \ No newline at end of file +!function(){"use strict";var e={274:function(e,t,n){var r,o=(r=n(521))&&r.__esModule?r:{default:r};document.addEventListener("DOMContentLoaded",(()=>{(0,o.default)()}))},521:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(648)),o=u(n(809)),l=u(n(852)),i=u(n(117)),a=u(n(606));function u(e){return e&&e.__esModule?e:{default:e}}var s=()=>{r.default.component.registerMany({LinkPicker:o.default,LinkField:l.default,"LinkModal.FormBuilderModal":i.default,"LinkModal.InsertMediaModal":a.default})};t.default=s},852:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.LinkFieldContext=void 0;var r=O(n(363)),o=n(827),l=n(624),i=(n(648),m(n(42))),a=m(n(809)),u=m(n(734)),s=m(n(686)),d=m(n(697)),f=O(n(123)),c=m(n(159)),p=m(n(510)),y=m(n(86)),v=m(n(754)),k=m(n(872));function m(e){return e&&e.__esModule?e:{default:e}}function _(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(_=function(e){return e?n:t})(e)}function O(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_(t);if(n&&n.has(e))return n.get(e);var r={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var i=o?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}const g=(0,r.createContext)(null);t.LinkFieldContext=g;const h="SilverStripe\\LinkField\\Controllers\\LinkFieldController",b=e=>{var t;let{value:n=null,onChange:o,types:l=[],actions:i,isMulti:s=!1,ownerID:f,ownerClass:y,ownerRelation:m}=e;const[_,O]=(0,r.useState)({}),[b,M]=(0,r.useState)(0);let w=n;Array.isArray(w)||("number"==typeof w&&0!=w&&(w=[w]),w||(w=[])),(0,r.useEffect)((()=>{if(!b&&w.length>0){const e=[];for(const t of w)e.push(`itemIDs[]=${t}`);const t=`${p.default.getSection(h).form.linkForm.dataUrl}?${e.join("&")}`;c.default.get(t).then((e=>e.json())).then((e=>{O(e)}))}}),[b,n&&n.length]);const C=()=>{M(0)},j=e=>{M(0);const t=[...w];t.includes(e)||t.push(e),o(s?t:t[0]),i.toasts.success(v.default._t("LinkField.SAVE_SUCCESS","Saved link"))},R=e=>{let t=`${p.default.getSection(h).form.linkForm.deleteUrl}/${e}`;const n=k.default.parse(t),r=qs.parse(n.query);r.ownerID=f,r.ownerClass=y,r.ownerRelation=m,t=k.default.format({...n,search:qs.stringify(r)}),c.default.delete(t,{},{"X-SecurityID":p.default.get("SecurityID")}).then((()=>{i.toasts.success(v.default._t("LinkField.DELETE_SUCCESS","Deleted link"))})).catch((()=>{i.toasts.error(v.default._t("LinkField.DELETE_ERROR","Failed to delete link"))}));const l={..._};delete l[e],O(l),o(s?Object.keys(l):0)},D=s||0===Object.keys(_).length,E=Boolean(b);return r.default.createElement(g.Provider,{value:{ownerID:f,ownerClass:y,ownerRelation:m}},D&&r.default.createElement(a.default,{onModalSuccess:j,onModalClosed:C,types:l}),r.default.createElement("div",null," ",(()=>{const e=[];for(const s of w){var t,n,o,i,a;if(!_[s])continue;const d=l.hasOwnProperty(null===(t=_[s])||void 0===t?void 0:t.typeKey)?l[null===(n=_[s])||void 0===n?void 0:n.typeKey]:{};e.push(r.default.createElement(u.default,{key:s,id:s,title:null===(o=_[s])||void 0===o?void 0:o.Title,description:null===(i=_[s])||void 0===i?void 0:i.description,versionState:null===(a=_[s])||void 0===a?void 0:a.versionState,typeTitle:d.title||"",onClear:R,onClick:()=>{M(s)}}))}return e})()," "),E&&r.default.createElement(d.default,{types:l,typeKey:null===(t=_[b])||void 0===t?void 0:t.typeKey,isOpen:Boolean(b),onSuccess:j,onClosed:C,linkID:b,ownerID:f,ownerClass:y,ownerRelation:m}))};b.propTypes={value:y.default.oneOfType([y.default.arrayOf(y.default.number),y.default.number]),onChange:y.default.func.isRequired,types:y.default.objectOf(s.default).isRequired,actions:y.default.object.isRequired,isMulti:y.default.bool,ownerID:y.default.number.isRequired,ownerClass:y.default.string.isRequired,ownerRelation:y.default.string.isRequired};var M=(0,o.compose)(i.default,(0,l.connect)(null,(e=>({actions:{toasts:(0,o.bindActionCreators)(f,e)}}))))(b);t.default=M},606:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;s(n(754));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={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var i=o?Object.getOwnPropertyDescriptor(e,l):null;i&&(i.get||i.set)?Object.defineProperty(r,l,i):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),o=s(n(475)),l=n(624),i=s(n(686)),a=s(n(86));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)}function s(e){return e&&e.__esModule?e:{default:e}}function d(){return d=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let{type:t,editing:n,data:l,actions:i,onSubmit:a,...u}=e;if(!t)return!1;(0,r.useEffect)((()=>{n?i.initModal():i.reset()}),[n]);const s=l?{ID:l.FileID,Description:l.Title,TargetBlank:!!l.OpenInNew}:{};return r.default.createElement(o.default,d({isOpen:n,type:"insert-link",title:!1,bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:s,onInsert:e=>{let{ID:n,Description:r,TargetBlank:o}=e;return a({FileID:n,Title:r,OpenInNew:o,typeKey:t.key},"",(()=>{}))}},u))};f.propTypes={type:i.default.isRequired,editing:a.default.bool.isRequired,data:a.default.object.isRequired,actions:a.default.object.isRequired,onClick:a.default.func.isRequired};var c=(0,l.connect)((function(){return{}}),(function(e){return{actions:{initModal:()=>e({type:"INIT_FORM_SCHEMA_STACK",payload:{formSchema:{type:"insert-link",nextType:"admin"}}}),reset:()=>e({type:"RESET"})}}}))(f);t.default=c},117: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={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var i=o?Object.getOwnPropertyDescriptor(e,l):null;i&&(i.get||i.set)?Object.defineProperty(r,l,i):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),o=d(n(912)),l=n(852),i=d(n(872)),a=d(n(902)),u=d(n(510)),s=d(n(86));function d(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)}const c=(e,t)=>{const{schemaUrl:n}=u.default.getSection("SilverStripe\\LinkField\\Controllers\\LinkFieldController").form.linkForm,o=i.default.parse(n),s=a.default.parse(o.query);s.typeKey=e;const{ownerID:d,ownerClass:f,ownerRelation:c}=(0,r.useContext)(l.LinkFieldContext);s.ownerID=d,s.ownerClass=f,s.ownerRelation=c;for(const e of["href","path","pathname"])o[e]=`${o[e]}/${t}`;return i.default.format({...o,search:a.default.stringify(s)})},p=e=>{let{typeTitle:t,typeKey:n,linkID:l=0,isOpen:i,onSuccess:a,onClosed:u}=e;if(!n)return!1;return r.default.createElement(o.default,{title:t,isOpen:i,schemaUrl:c(n,l),identifier:"Link.EditingLinkInfo",onSubmit:async(e,t,n)=>{const r=await n();if(!r.id.match(/\/schema\/linkfield\/([0-9]+)/)){const e=r.id.match(/\/linkForm\/([0-9]+)/),t=parseInt(e[1],10);a(t)}return Promise.resolve()},onClosed:u})};p.propTypes={typeTitle:s.default.string.isRequired,typeKey:s.default.string.isRequired,linkID:s.default.number,isOpen:s.default.bool.isRequired,onSuccess:s.default.func.isRequired,onClosed:s.default.func.isRequired};var y=p;t.default=y},809: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={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var i=o?Object.getOwnPropertyDescriptor(e,l):null;i&&(i.get||i.set)?Object.defineProperty(r,l,i):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),o=s(n(86)),l=s(n(820)),i=s(n(97)),a=s(n(686)),u=s(n(697));function s(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 f=e=>{let{types:t,onModalSuccess:n,onModalClosed:o}=e;const[a,s]=(0,r.useState)(""),d=""!==a,f=(0,l.default)("link-picker","form-control"),c=Object.values(t);return r.default.createElement("div",{className:f},r.default.createElement(i.default,{types:c,onSelect:e=>{s(e)}}),d&&r.default.createElement(u.default,{types:t,typeKey:a,isOpen:d,onSuccess:e=>{s(""),n(e)},onClosed:()=>{"function"==typeof o&&o(),s("")}}))};t.Component=f,f.propTypes={types:o.default.objectOf(a.default).isRequired,onModalSuccess:o.default.func.isRequired,onModalClosed:o.default.func};var c=f;t.default=c},97:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=s(n(754)),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={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var i=o?Object.getOwnPropertyDescriptor(e,l):null;i&&(i.get||i.set)?Object.defineProperty(r,l,i):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),l=s(n(86)),i=n(127),a=s(n(686));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)}function s(e){return e&&e.__esModule?e:{default:e}}const d=e=>{let{types:t,onSelect:n}=e;const[l,a]=(0,o.useState)(!1);return o.default.createElement(i.Dropdown,{isOpen:l,toggle:()=>a((e=>!e)),className:"link-picker__menu"},o.default.createElement(i.DropdownToggle,{className:"link-picker__menu-toggle font-icon-plus-1",caret:!0},r.default._t("LinkField.ADD_LINK","Add Link")),o.default.createElement(i.DropdownMenu,null,t.map((e=>{let{key:t,title:r}=e;return o.default.createElement(i.DropdownItem,{key:t,onClick:()=>n(t)},r)}))))};d.propTypes={types:l.default.arrayOf(a.default).isRequired,onSelect:l.default.func.isRequired};var f=d;t.default=f},734:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(820)),o=u(n(754)),l=u(n(363)),i=u(n(86)),a=n(127);function u(e){return e&&e.__esModule?e:{default:e}}const s=e=>t=>{t.nativeEvent.stopImmediatePropagation(),t.preventDefault(),t.nativeEvent.preventDefault(),t.stopPropagation(),e&&e()},d=e=>{let{id:t,title:n,description:i,versionState:u,typeTitle:d,onClear:f,onClick:c}=e;const p={"link-picker__link":!0,"form-control":!0};u&&(p[` link-picker__link--${u}`]=!0),n&&n.length>25&&(n=n.substring(0,25)+"...");const y=(0,r.default)(p);return l.default.createElement("div",{className:y},l.default.createElement(a.Button,{className:"link-picker__button font-icon-link",color:"secondary",onClick:s(c)},l.default.createElement("div",{className:"link-picker__link-detail"},l.default.createElement("div",{className:"link-picker__title"},l.default.createElement("span",{className:"link-picker__title-text"},n),(e=>{let t="",n="";if("draft"===e)t=o.default._t("LinkField.LINK_DRAFT_TITLE","Link has draft changes"),n=o.default._t("LinkField.LINK_DRAFT_LABEL","Draft");else{if("modified"!==e)return null;t=o.default._t("LinkField.LINK_MODIFIED_TITLE","Link has unpublished changes"),n=o.default._t("LinkField.LINK_MODIFIED_LABEL","Modified")}const i=(0,r.default)("badge",`status-${e}`);return l.default.createElement("span",{className:i,title:t},n)})(u)),l.default.createElement("small",{className:"link-picker__type"},d,": ",l.default.createElement("span",{className:"link-picker__url"},i)))),l.default.createElement(a.Button,{className:"link-picker__clear",color:"link",onClick:s((()=>f(t)))},o.default._t("LinkField.CLEAR","Clear")))};d.propTypes={id:i.default.number.isRequired,title:i.default.string,description:i.default.string,versionState:i.default.string,typeTitle:i.default.string.isRequired,onClear:i.default.func.isRequired,onClick:i.default.func.isRequired};var f=d;t.default=f},697:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=a(n(363)),o=n(648),l=a(n(86)),i=a(n(686));function a(e){return e&&e.__esModule?e:{default:e}}const u=e=>{let{types:t,typeKey:n,linkID:l=0,isOpen:i,onSuccess:a,onClosed:u}=e;if(!n)return!1;const s=t.hasOwnProperty(n)?t[n]:{},d=s&&s.hasOwnProperty("handlerName")?s.handlerName:"FormBuilderModal",f=(0,o.loadComponent)(`LinkModal.${d}`);return r.default.createElement(f,{typeTitle:s.title||"",typeKey:n,linkID:l,isOpen:i,onSuccess:a,onClosed:u})};u.propTypes={types:l.default.objectOf(i.default).isRequired,typeKey:l.default.string.isRequired,linkID:l.default.number,isOpen:l.default.bool.isRequired,onSuccess:l.default.func.isRequired,onClosed:l.default.func.isRequired};var s=u;t.default=s},41:function(e,t,n){var r=a(n(311)),o=a(n(363)),l=a(n(691)),i=n(648);function a(e){return e&&e.__esModule?e:{default:e}}function u(){return u=Object.assign?Object.assign.bind():function(e){for(var t=1;t{e(".js-injector-boot .entwine-linkfield").entwine({Component:null,Root:null,onmatch(){const e=this.closest(".cms-content").attr("id"),t=e?{context:e}:{},n=this.data("schema-component"),r=(0,i.loadComponent)(n,t);this.setComponent(r),this.setRoot(l.default.createRoot(this[0])),this._super(),this.refresh()},refresh(){const e=this.getProps();this.getInputField().val(e.value);const t=this.getComponent();this.getRoot().render(o.default.createElement(t,u({},e,{noHolder:!0})))},handleChange(e){this.getInputField().data("value",e),this.refresh()},getProps(){const e=this.getInputField();return{value:e.data("value"),ownerID:e.data("owner-id"),ownerClass:e.data("owner-class"),ownerRelation:e.data("owner-relation"),onChange:this.handleChange.bind(this),isMulti:this.data("is-multi")??!1,types:this.data("types")??[]}},getInputField(){const t=this.data("field-id");return e(`#${t}`)},onunmatch(){const e=this.getRoot();e&&e.unmount()}})}))},686:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=(r=n(86))&&r.__esModule?r:{default:r};var l=o.default.shape({key:o.default.string.isRequired,title:o.default.string.isRequired});t.default=l},159:function(e){e.exports=Backend},510:function(e){e.exports=Config},42:function(e){e.exports=FieldHolder},912:function(e){e.exports=FormBuilderModal},648:function(e){e.exports=Injector},475:function(e){e.exports=InsertMediaModal},872:function(e){e.exports=NodeUrl},86:function(e){e.exports=PropTypes},363:function(e){e.exports=React},691:function(e){e.exports=ReactDomClient},624:function(e){e.exports=ReactRedux},127:function(e){e.exports=Reactstrap},827:function(e){e.exports=Redux},123:function(e){e.exports=ToastsActions},820:function(e){e.exports=classnames},754:function(e){e.exports=i18n},311:function(e){e.exports=jQuery},902:function(e){e.exports=qs}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var l=t[r]={exports:{}};return e[r](l,l.exports,n),l.exports}n(274),n(41)}(); \ No newline at end of file diff --git a/client/src/components/LinkField/LinkField.js b/client/src/components/LinkField/LinkField.js index d4b493dc..6dd9a417 100644 --- a/client/src/components/LinkField/LinkField.js +++ b/client/src/components/LinkField/LinkField.js @@ -1,5 +1,5 @@ /* eslint-disable */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, createContext } from 'react'; import { bindActionCreators, compose } from 'redux'; import { connect } from 'react-redux'; import { injectGraphql } from 'lib/Injector'; @@ -13,6 +13,9 @@ import backend from 'lib/Backend'; import Config from 'lib/Config'; import PropTypes from 'prop-types'; import i18n from 'i18n'; +import url from 'url'; + +export const LinkFieldContext = createContext(null); // section used in window.ss config const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController'; @@ -23,8 +26,20 @@ const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController'; * types - types of the Link passed from LinkField entwine * actions - object of redux actions * isMulti - whether this field handles multiple links or not + * ownerID - ID of the owner DataObject + * ownerClass - class name of the owner DataObject + * ownerRelation - name of the relation on the owner DataObject */ -const LinkField = ({ value = null, onChange, types = [], actions, isMulti = false }) => { +const LinkField = ({ + value = null, + onChange, + types = [], + actions, + isMulti = false, + ownerID, + ownerClass, + ownerRelation, +}) => { const [data, setData] = useState({}); const [editingID, setEditingID] = useState(0); @@ -93,7 +108,13 @@ const LinkField = ({ value = null, onChange, types = [], actions, isMulti = fals * Update the component when the 'Clear' button in the LinkPicker is clicked */ const onClear = (linkID) => { - const endpoint = `${Config.getSection(section).form.linkForm.deleteUrl}/${linkID}`; + let endpoint = `${Config.getSection(section).form.linkForm.deleteUrl}/${linkID}`; + const parsedURL = url.parse(endpoint); + const parsedQs = qs.parse(parsedURL.query); + parsedQs.ownerID = ownerID; + parsedQs.ownerClass = ownerClass; + parsedQs.ownerRelation = ownerRelation; + endpoint = url.format({ ...parsedURL, search: qs.stringify(parsedQs)}); // CSRF token 'X-SecurityID' headers needs to be present for destructive requests backend.delete(endpoint, {}, { 'X-SecurityID': Config.get('SecurityID') }) .then(() => { @@ -153,7 +174,7 @@ const LinkField = ({ value = null, onChange, types = [], actions, isMulti = fals const renderPicker = isMulti || Object.keys(data).length === 0; const renderModal = Boolean(editingID); - return <> + return { renderPicker && }
{ renderLinks() }
{ renderModal && } - ; +
; }; LinkField.propTypes = { @@ -174,6 +198,9 @@ LinkField.propTypes = { types: PropTypes.objectOf(LinkType).isRequired, actions: PropTypes.object.isRequired, isMulti: PropTypes.bool, + ownerID: PropTypes.number.isRequired, + ownerClass: PropTypes.string.isRequired, + ownerRelation: PropTypes.string.isRequired, }; // redux actions loaded into props - used to get toast notifications diff --git a/client/src/components/LinkModal/LinkModal.js b/client/src/components/LinkModal/LinkModal.js index 4d518ec8..d590a6cd 100644 --- a/client/src/components/LinkModal/LinkModal.js +++ b/client/src/components/LinkModal/LinkModal.js @@ -1,6 +1,7 @@ /* eslint-disable */ -import React from 'react'; +import React, { useContext } from 'react' import FormBuilderModal from 'components/FormBuilderModal/FormBuilderModal'; +import { LinkFieldContext } from 'components/LinkField/LinkField'; import url from 'url'; import qs from 'qs'; import Config from 'lib/Config'; @@ -11,13 +12,17 @@ const buildSchemaUrl = (typeKey, linkID) => { const parsedURL = url.parse(schemaUrl); const parsedQs = qs.parse(parsedURL.query); parsedQs.typeKey = typeKey; + const { ownerID, ownerClass, ownerRelation } = useContext(LinkFieldContext); + parsedQs.ownerID = ownerID; + parsedQs.ownerClass = ownerClass; + parsedQs.ownerRelation = ownerRelation; for (const prop of ['href', 'path', 'pathname']) { parsedURL[prop] = `${parsedURL[prop]}/${linkID}`; } return url.format({ ...parsedURL, search: qs.stringify(parsedQs)}); } -const LinkModal = ({ typeTitle, typeKey, linkID = 0, isOpen, onSuccess, onClosed}) => { +const LinkModal = ({ typeTitle, typeKey, linkID = 0, isOpen, onSuccess, onClosed }) => { if (!typeKey) { return false; } diff --git a/client/src/entwine/LinkField.js b/client/src/entwine/LinkField.js index ccd45f9f..af620ffb 100644 --- a/client/src/entwine/LinkField.js +++ b/client/src/entwine/LinkField.js @@ -45,9 +45,12 @@ jQuery.entwine('ss', ($) => { * @returns {Object} */ getProps() { - const value = this.getInputField().data('value'); + const inputField = this.getInputField(); return { - value, + value: inputField.data('value'), + ownerID: inputField.data('owner-id'), + ownerClass: inputField.data('owner-class'), + ownerRelation: inputField.data('owner-relation'), onChange: this.handleChange.bind(this), isMulti: this.data('is-multi') ?? false, types: this.data('types') ?? [], diff --git a/src/Controllers/LinkFieldController.php b/src/Controllers/LinkFieldController.php index 27ff9db5..f5884c4c 100644 --- a/src/Controllers/LinkFieldController.php +++ b/src/Controllers/LinkFieldController.php @@ -16,11 +16,13 @@ use SilverStripe\ORM\ValidationResult; use SilverStripe\Control\Controller; use SilverStripe\Control\HTTPRequest; +use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\HiddenField; use SilverStripe\LinkField\Form\LinkField; use SilverStripe\LinkField\Services\LinkTypeService; use SilverStripe\ORM\DataList; +use SilverStripe\ORM\DataObject; class LinkFieldController extends LeftAndMain { @@ -134,6 +136,16 @@ public function linkDelete(): HTTPResponse } // delete() will also delete any published version immediately $link->delete(); + // Update owner object if this Link is on a has_one relation on the owner + $owner = $this->ownerFromRequest(); + $ownerRelation = $this->ownerRelationFromRequest(); + $config = Config::forClass($owner->ClassName); + $hasOne = $config->get('has_one'); + if (array_key_exists($ownerRelation, $hasOne) && $owner->canEdit()) { + $owner->$ownerRelation = null; + $owner->write(); + } + // Send response $response = $this->getResponse(); $response->addHeader('Content-type', 'application/json'); $response->setBody(json_encode(['success' => true])); @@ -214,6 +226,16 @@ public function save(array $data, Form $form): HTTPResponse $link->write(); } + // Update owner object if this Link is on a has_one relation on the owner + $owner = $this->ownerFromRequest(); + $ownerRelation = $this->ownerRelationFromRequest(); + $config = Config::forClass($owner->ClassName); + $hasOne = $config->get('has_one'); + if (array_key_exists($ownerRelation, $hasOne) && $owner->canEdit()) { + $owner->$ownerRelation = $link; + $owner->write(); + } + // Create a new Form so that it has the correct ID for the DataObject when creating // a new DataObject, as well as anything else on the DataObject that may have been // updated in an extension hook. We do this so that the FormSchema state is correct @@ -240,6 +262,15 @@ private function createLinkForm(Link $link, string $operation): Form /** @var Form $form */ $form = $formFactory->getForm($this, $name, ['Record' => $link]); + // Add hidden form fields for OwnerID, OwnerClass and OwnerRelation + if ($operation === 'create') { + $owner = $this->ownerFromRequest(); + $form->Fields()->push(HiddenField::create('OwnerID')->setValue($owner->ID)); + $form->Fields()->push(HiddenField::create('OwnerClass')->setValue($owner->ClassName)); + $ownerRelation = $this->ownerRelationFromRequest(); + $form->Fields()->push(HiddenField::create('OwnerRelation')->setValue($ownerRelation)); + } + // Set where the form is submitted to $typeKey = LinkTypeService::create()->keyByClassName($link->ClassName); $form->setFormAction($this->Link("linkForm/$id?typeKey=$typeKey")); @@ -357,4 +388,58 @@ private function typeKeyFromRequest(): string } return $typeKey; } + + /** + * Get the owner based on the query string params ownerID, ownerClass, ownerRelation + * OR the POST vars OwnerID, OwnerClass, OwnerRelation + */ + private function ownerFromRequest(): DataObject + { + $request = $this->getRequest(); + $ownerID = (int) ($request->getVar('ownerID') ?: $request->postVar('OwnerID')); + if ($ownerID === 0) { + $this->jsonError(404, _t('LinkField.INVALID_OWNER_ID', 'Invalid ownerID')); + } + $ownerClass = $request->getVar('ownerClass') ?: $request->postVar('OwnerClass'); + if (!is_a($ownerClass, DataObject::class, true)) { + $this->jsonError(404, _t('LinkField.INVALID_OWNER_CLASS', 'Invalid ownerClass')); + } + $ownerRelation = $this->ownerRelationFromRequest(); + $config = Config::forClass($ownerClass); + $hasOne = $config->get('has_one'); + $hasMany = $config->get('has_many'); + $matchedRelation = false; + foreach ([$hasOne, $hasMany] as $property) { + if (!array_key_exists($ownerRelation, $property)) { + continue; + } + $className = $property[$ownerRelation]; + if (is_a($className, Link::class, true)) { + $matchedRelation = true; + break; + } + } + if ($matchedRelation) { + /** @var DataObject $ownerClass */ + $owner = $ownerClass::get()->byID($ownerID); + if ($owner) { + return $owner; + } + } + $this->jsonError(404, _t('LinkField.INVALID_OWNER', 'Invalid Owner')); + } + + /** + * Get the owner relation based on the query string param ownerRelation + * OR the POST var OwnerRelation + */ + private function ownerRelationFromRequest(): string + { + $request = $this->getRequest(); + $ownerRelation = $request->getVar('ownerRelation') ?: $request->postVar('OwnerRelation'); + if (!$ownerRelation) { + $this->jsonError(404, _t('LinkField.INVALID_OWNER_RELATION', 'Invalid ownerRelation')); + } + return $ownerRelation; + } } diff --git a/src/Form/LinkField.php b/src/Form/LinkField.php index 62ffbe12..bd96c6d2 100644 --- a/src/Form/LinkField.php +++ b/src/Form/LinkField.php @@ -3,11 +3,15 @@ namespace SilverStripe\LinkField\Form; use LogicException; +use SilverStripe\CMS\Controllers\CMSPageEditController; +use SilverStripe\CMS\Controllers\ContentController; +use SilverStripe\CMS\Model\SiteTree; use SilverStripe\Forms\FormField; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObjectInterface; use SilverStripe\LinkField\Models\Link; use SilverStripe\LinkField\Form\Traits\AllowedLinkClassesTrait; +use SilverStripe\LinkField\Form\Traits\LinkFieldGetOwnerTrait; /** * Allows CMS users to edit a Link object. @@ -15,6 +19,7 @@ class LinkField extends FormField { use AllowedLinkClassesTrait; + use LinkFieldGetOwnerTrait; protected $schemaComponent = 'LinkField'; @@ -31,39 +36,43 @@ public function setValue($value, $data = null) return parent::setValue($id, $data); } - /** - * @param DataObject|DataObjectInterface $record - A DataObject such as a Page - * @return $this - */ - public function saveInto(DataObjectInterface $record) - { - // Check required relation details are available - $fieldname = $this->getName(); - if (!$fieldname) { - throw new LogicException('LinkField must have a name'); - } + // /** + // * @param DataObject|DataObjectInterface $record - A DataObject such as a Page + // * @return $this + // */ + // public function saveInto(DataObjectInterface $record) + // { + // // Check required relation details are available + // $fieldname = $this->getName(); + // if (!$fieldname) { + // throw new LogicException('LinkField must have a name'); + // } - $linkID = $this->dataValue(); - $dbColumn = $fieldname . 'ID'; - $record->$dbColumn = $linkID; + // $linkID = $this->dataValue(); + // $dbColumn = $fieldname . 'ID'; + // $record->$dbColumn = $linkID; - // Store the record as the owner of the link. - // Required for permission checks, etc. - $link = Link::get()->byID($linkID); - if ($link) { - $link->OwnerID = $record->ID; - $link->OwnerClass = $record->ClassName; - $link->OwnerRelation = $fieldname; - $link->write(); - } + // // Store the record as the owner of the link. + // // Required for permission checks, etc. + // $link = Link::get()->byID($linkID); + // if ($link) { + // $link->OwnerID = $record->ID; + // $link->OwnerClass = $record->ClassName; + // $link->OwnerRelation = $fieldname; + // $link->write(); + // } - return $this; - } + // return $this; + // } protected function getDefaultAttributes(): array { $attributes = parent::getDefaultAttributes(); $attributes['data-value'] = $this->Value(); + $ownerFields = $this->getOwnerFields(); + $attributes['data-owner-id'] = $ownerFields['ID']; + $attributes['data-owner-class'] = $ownerFields['Class']; + $attributes['data-owner-relation'] = $ownerFields['Relation']; return $attributes; } @@ -71,6 +80,11 @@ public function getSchemaDataDefaults() { $data = parent::getSchemaDataDefaults(); $data['types'] = json_decode($this->getTypesProps()); + // TODO In LinkField this is in getSchemaStateDefaults() - is that wrong? + $ownerFields = $this->getOwnerFields(); + $data['ownerID'] = $ownerFields['ID']; + $data['ownerClass'] = $ownerFields['Class']; + $data['ownerRelation'] = $ownerFields['Relation']; return $data; } } diff --git a/src/Form/MultiLinkField.php b/src/Form/MultiLinkField.php index 76632909..c44aa5c5 100644 --- a/src/Form/MultiLinkField.php +++ b/src/Form/MultiLinkField.php @@ -5,6 +5,7 @@ use LogicException; use SilverStripe\Forms\FormField; use SilverStripe\LinkField\Form\Traits\AllowedLinkClassesTrait; +use SilverStripe\LinkField\Form\Traits\LinkFieldGetOwnerTrait; use SilverStripe\ORM\DataObjectInterface; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\Relation; @@ -18,6 +19,7 @@ class MultiLinkField extends FormField { use AllowedLinkClassesTrait; + use LinkFieldGetOwnerTrait; protected $schemaComponent = 'LinkField'; @@ -38,27 +40,27 @@ public function setValue($value, $data = null) return parent::setValue($ids, $data); } - public function saveInto(DataObjectInterface $record) - { - $fieldName = $this->getName(); - if (!$fieldName) { - throw new LogicException('LinkField must have a name'); - } + // public function saveInto(DataObjectInterface $record) + // { + // $fieldName = $this->getName(); + // if (!$fieldName) { + // throw new LogicException('LinkField must have a name'); + // } - $relation = $record->hasMethod($fieldName) ? $record->$fieldName() : null; - if (!$relation) { - throw new LogicException("{$record->ClassName} is missing the relation '$fieldName'"); - } + // $relation = $record->hasMethod($fieldName) ? $record->$fieldName() : null; + // if (!$relation) { + // throw new LogicException("{$record->ClassName} is missing the relation '$fieldName'"); + // } - // Use RelationList rather than Relation here since some Relation classes don't allow setting value - but RelationList does. - if (!($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) { - throw new LogicException("'$fieldName()' method on {$record->ClassName} doesn't return a relation list"); - } else { - $relation->setByIDList($this->getValueArray() ?? []); - } + // // Use RelationList rather than Relation here since some Relation classes don't allow setting value - but RelationList does. + // if (!($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) { + // throw new LogicException("'$fieldName()' method on {$record->ClassName} doesn't return a relation list"); + // } else { + // $relation->setByIDList($this->getValueArray() ?? []); + // } - return $this; - } + // return $this; + // } public function getSchemaDataDefaults() { @@ -72,6 +74,11 @@ public function getSchemaStateDefaults() { $data = parent::getSchemaStateDefaults(); $data['value'] = $this->getValueArray(); + // TODO In LinkField this is in getSchemaDataDefaults() - is that wrong? + $ownerFields = $this->getOwnerFields(); + $data['ownerID'] = $ownerFields['ID']; + $data['ownerClass'] = $ownerFields['Class']; + $data['ownerRelation'] = $ownerFields['Relation']; return $data; } @@ -79,6 +86,10 @@ protected function getDefaultAttributes(): array { $attributes = parent::getDefaultAttributes(); $attributes['data-value'] = $this->getValueArray(); + $ownerFields = $this->getOwnerFields(); + $attributes['data-owner-id'] = $ownerFields['ID']; + $attributes['data-owner-class'] = $ownerFields['Class']; + $attributes['data-owner-relation'] = $ownerFields['Relation']; return $attributes; } diff --git a/src/Form/Traits/LinkFieldGetOwnerTrait.php b/src/Form/Traits/LinkFieldGetOwnerTrait.php new file mode 100644 index 00000000..e5f1c88d --- /dev/null +++ b/src/Form/Traits/LinkFieldGetOwnerTrait.php @@ -0,0 +1,31 @@ +getForm(); + $owner = $form->getRecord(); + if (!$owner) { + throw new LogicException('Could not determine owner from form'); + } + return $owner; + } + + private function getOwnerFields(): array + { + $owner = $this->getOwner(); + return [ + 'ID' => $owner->ID, + 'Class' => $owner::class, + 'Relation' => $this->getName(), + ]; + } +} diff --git a/tests/php/Form/LinkFieldTest.php b/tests/php/Form/LinkFieldTest.php index 4fb94aec..3e3b849c 100644 --- a/tests/php/Form/LinkFieldTest.php +++ b/tests/php/Form/LinkFieldTest.php @@ -15,29 +15,29 @@ class LinkFieldTest extends SapphireTest LinkOwner::class, ]; - /** - * When we save a link into the has_one of a record, we also need to save - * the Owner has_one on the link itself. - */ - public function testSaveInto() - { - // Prepare fixtures (need new records for this) - $field = new LinkField('Link'); - $link = new Link(); - $link->write(); - $owner = new LinkOwner(); - $owner->write(); + // /** + // * When we save a link into the has_one of a record, we also need to save + // * the Owner has_one on the link itself. + // */ + // public function testSaveInto() + // { + // // Prepare fixtures (need new records for this) + // $field = new LinkField('Link'); + // $link = new Link(); + // $link->write(); + // $owner = new LinkOwner(); + // $owner->write(); - // Save link into owner - $field->setValue($link->ID); - $field->saveInto($owner); - // Get the link again - the new values are in the DB. - $link = Link::get()->byID($link->ID); + // // Save link into owner + // $field->setValue($link->ID); + // $field->saveInto($owner); + // // Get the link again - the new values are in the DB. + // $link = Link::get()->byID($link->ID); - // Validate - $this->assertSame($link->ID, $owner->LinkID); - $this->assertSame($owner->ID, $link->OwnerID); - $this->assertSame($owner->ClassName, $link->OwnerClass); - $this->assertSame('Link', $link->OwnerRelation); - } + // // Validate + // $this->assertSame($link->ID, $owner->LinkID); + // $this->assertSame($owner->ID, $link->OwnerID); + // $this->assertSame($owner->ClassName, $link->OwnerClass); + // $this->assertSame('Link', $link->OwnerRelation); + // } }