From 9d8137425a12b0da9759924186cb977a6071a493 Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Tue, 19 Dec 2023 12:40:19 +1300 Subject: [PATCH] ENH Tidy up permissions --- client/dist/js/bundle.js | 2 +- client/dist/styles/bundle.css | 2 +- client/src/components/LinkField/LinkField.js | 6 +- .../src/components/LinkPicker/LinkPicker.js | 13 +- .../src/components/LinkPicker/LinkPicker.scss | 6 + .../components/LinkPicker/LinkPickerMenu.js | 38 +-- .../components/LinkPicker/LinkPickerTitle.js | 16 +- client/src/entwine/LinkField.js | 1 + src/Controllers/LinkFieldController.php | 1 + src/Form/LinkField.php | 8 + src/Form/MultiLinkField.php | 3 + src/Models/FileLink.php | 6 +- src/Models/SiteTreeLink.php | 14 +- .../LinkField/Form/MultiLinkField.ss | 2 +- .../Controllers/LinkFieldControllerTest.php | 259 ++++++++++-------- tests/php/Models/FileLinkTest.php | 38 +++ .../Models/FileLinkTest/TestFileCanView.php | 14 + .../FileLinkTest/TestFileCannotView.php | 14 + tests/php/Models/FileLinkTest/file-a.png | Bin 0 -> 3083 bytes tests/php/Models/FileLinkTest/file-b.png | Bin 0 -> 3083 bytes tests/php/Models/SiteTreeLinkTest.php | 48 ++++ .../SiteTreeLinkTest/TestSiteTreeCanView.php | 14 + .../TestSiteTreeCannotView.php | 14 + 23 files changed, 380 insertions(+), 139 deletions(-) create mode 100644 tests/php/Models/FileLinkTest.php create mode 100644 tests/php/Models/FileLinkTest/TestFileCanView.php create mode 100644 tests/php/Models/FileLinkTest/TestFileCannotView.php create mode 100644 tests/php/Models/FileLinkTest/file-a.png create mode 100644 tests/php/Models/FileLinkTest/file-b.png create mode 100644 tests/php/Models/SiteTreeLinkTest.php create mode 100644 tests/php/Models/SiteTreeLinkTest/TestSiteTreeCanView.php create mode 100644 tests/php/Models/SiteTreeLinkTest/TestSiteTreeCannotView.php diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 0a48133f..fa7a437f 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=o(n(521)),l=o(n(154));function o(e){return e&&e.__esModule?e:{default:e}}document.addEventListener("DOMContentLoaded",(()=>{(0,r.default)(),(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)),a=u(n(117)),i=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":a.default,"LinkModal.InsertMediaModal":i.default})};t.default=s},154:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=o(n(648)),l=o(n(689));function o(e){return e&&e.__esModule?e:{default:e}}var a=()=>{r.default.query.register("readLinkTypes",l.default)};t.default=a},852:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=g(n(363)),l=n(827),o=n(624),a=n(648),i=m(n(42)),u=m(n(809)),s=m(n(734)),d=m(n(686)),f=m(n(697)),c=g(n(123)),p=m(n(159)),y=m(n(510)),v=m(n(86)),k=m(n(754));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 g(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={},l=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var a=l?Object.getOwnPropertyDescriptor(e,o):null;a&&(a.get||a.set)?Object.defineProperty(r,o,a):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:a,isMulti:i=!1}=e;const[d,c]=(0,r.useState)({}),[v,m]=(0,r.useState)(0);let _=n;Array.isArray(_)||("number"==typeof _&&0!=_&&(_=[_]),_||(_=[])),(0,r.useEffect)((()=>{if(!v&&_.length>0){const e=[];for(const t of _)e.push(`itemIDs[]=${t}`);const t=`${y.default.getSection(h).form.linkForm.dataUrl}?${e.join("&")}`;p.default.get(t).then((e=>e.json())).then((e=>{c(e)}))}}),[v,n&&n.length]);const g=()=>{m(0)},O=e=>{m(0);const t=[..._];t.includes(e)||t.push(e),l(i?t:t[0]),a.toasts.success(k.default._t("LinkField.SAVE_SUCCESS","Saved link"))},b=e=>{const t=`${y.default.getSection(h).form.linkForm.deleteUrl}/${e}`;p.default.delete(t,{},{"X-SecurityID":y.default.get("SecurityID")}).then((()=>{a.toasts.success(k.default._t("LinkField.DELETE_SUCCESS","Deleted link"))})).catch((()=>{a.toasts.error(k.default._t("LinkField.DELETE_ERROR","Failed to delete link"))}));const n={...d};delete n[e],c(n),l(i?Object.keys(n):0)},M=i||0===Object.keys(d).length,j=Boolean(v);return r.default.createElement(r.default.Fragment,null,M&&r.default.createElement(u.default,{onModalSuccess:O,onModalClosed:g,types:o}),r.default.createElement("div",null," ",(()=>{const e=[];for(const u of _){var t,n,l,a,i;if(!d[u])continue;const f=o.hasOwnProperty(null===(t=d[u])||void 0===t?void 0:t.typeKey)?o[null===(n=d[u])||void 0===n?void 0:n.typeKey]:{};e.push(r.default.createElement(s.default,{key:u,id:u,title:null===(l=d[u])||void 0===l?void 0:l.Title,description:null===(a=d[u])||void 0===a?void 0:a.description,versionState:null===(i=d[u])||void 0===i?void 0:i.versionState,typeTitle:f.title||"",onClear:b,onClick:()=>{m(u)}}))}return e})()," "),j&&r.default.createElement(f.default,{types:o,typeKey:null===(t=d[v])||void 0===t?void 0:t.typeKey,isOpen:Boolean(v),onSuccess:O,onClosed:g,linkID:v}))};O.propTypes={value:v.default.oneOfType([v.default.arrayOf(v.default.number),v.default.number]),onChange:v.default.func.isRequired,types:v.default.objectOf(d.default).isRequired,actions:v.default.object.isRequired,isMulti:v.default.bool};var b=(0,l.compose)((0,a.injectGraphql)("readLinkTypes"),i.default,(0,o.connect)(null,(e=>({actions:{toasts:(0,l.bindActionCreators)(c,e)}}))))(O);t.default=b},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 a=l?Object.getOwnPropertyDescriptor(e,o):null;a&&(a.get||a.set)?Object.defineProperty(r,o,a):r[o]=e[o]}r.default=e,n&&n.set(e,r);return r}(n(363)),l=s(n(475)),o=n(624),a=s(n(686)),i=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:a,onSubmit:i,...u}=e;if(!t)return!1;(0,r.useEffect)((()=>{n?a.initModal():a.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 i({FileID:n,Title:r,OpenInNew:l,typeKey:t.key},"",(()=>{}))}},u))};f.propTypes={type:a.default.isRequired,editing:i.default.bool.isRequired,data:i.default.object.isRequired,actions:i.default.object.isRequired,onClick:i.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)),a=s(n(902)),i=s(n(510)),u=s(n(86));function s(e){return e&&e.__esModule?e:{default:e}}const d=(e,t)=>{const{schemaUrl:n}=i.default.getSection("SilverStripe\\LinkField\\Controllers\\LinkFieldController").form.linkForm,r=o.default.parse(n),l=a.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:a.default.stringify(l)})},f=e=>{let{typeTitle:t,typeKey:n,linkID:o=0,isOpen:a,onSuccess:i,onClosed:u}=e;if(!n)return!1;return r.default.createElement(l.default,{title:t,isOpen:a,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);i(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 a=l?Object.getOwnPropertyDescriptor(e,o):null;a&&(a.get||a.set)?Object.defineProperty(r,o,a):r[o]=e[o]}r.default=e,n&&n.set(e,r);return r}(n(363)),l=s(n(86)),o=s(n(820)),a=s(n(97)),i=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[i,s]=(0,r.useState)(""),d=""!==i,f=(0,o.default)("link-picker","form-control"),c=Object.values(t);return r.default.createElement("div",{className:f},r.default.createElement(a.default,{types:c,onSelect:e=>{s(e)}}),d&&r.default.createElement(u.default,{types:t,typeKey:i,isOpen:d,onSuccess:e=>{s(""),n(e)},onClosed:()=>{"function"==typeof l&&l(),s("")}}))};t.Component=f,f.propTypes={types:l.default.objectOf(i.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 a=l?Object.getOwnPropertyDescriptor(e,o):null;a&&(a.get||a.set)?Object.defineProperty(r,o,a):r[o]=e[o]}r.default=e,n&&n.set(e,r);return r}(n(363)),o=s(n(86)),a=n(127),i=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,i]=(0,l.useState)(!1);return l.default.createElement(a.Dropdown,{isOpen:o,toggle:()=>i((e=>!e)),className:"link-picker__menu"},l.default.createElement(a.DropdownToggle,{className:"link-picker__menu-toggle font-icon-plus-1",caret:!0},r.default._t("LinkField.ADD_LINK","Add Link")),l.default.createElement(a.DropdownMenu,null,t.map((e=>{let{key:t,title:r}=e;return l.default.createElement(a.DropdownItem,{key:t,onClick:()=>n(t)},r)}))))};d.propTypes={types:o.default.arrayOf(i.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)),a=u(n(86)),i=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:a,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(i.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 a=(0,r.default)("badge",`status-${e}`);return o.default.createElement("span",{className:a,title:t},n)})(u)),o.default.createElement("small",{className:"link-picker__type"},d,": ",o.default.createElement("span",{className:"link-picker__url"},a)))),o.default.createElement(i.Button,{className:"link-picker__clear",color:"link",onClick:s((()=>f(t)))},l.default._t("LinkField.CLEAR","Clear")))};d.propTypes={id:a.default.number.isRequired,title:a.default.string,description:a.default.string,versionState:a.default.string,typeTitle:a.default.string.isRequired,onClear:a.default.func.isRequired,onClick:a.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=i(n(363)),l=n(648),o=i(n(86)),a=i(n(686));function i(e){return e&&e.__esModule?e:{default:e}}const u=e=>{let{types:t,typeKey:n,linkID:o=0,isOpen:a,onSuccess:i,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:a,onSuccess:i,onClosed:u})};u.propTypes={types:o.default.objectOf(a.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=i(n(311)),l=i(n(363)),o=i(n(691)),a=n(648);function i(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,a.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}},getInputField(){const t=this.data("field-id");return e(`#${t}`)},onunmatch(){const e=this.getRoot();e&&e.unmount()}})}))},689:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n(648);const l={props(e){const{data:{error:t,readLinkTypes:n,loading:r}}=e,l=t&&t.graphQLErrors&&t.graphQLErrors.map((e=>e.message));return{loading:r,types:n?n.reduce(((e,t)=>({...e,[t.key]:t})),{}):{},graphQLErrors:l}}},{READ:o}=r.graphqlTemplates;var a={apolloConfig:l,templateName:o,pluralName:"LinkTypes",pagination:!1,params:{keys:"[ID]"},args:{root:{keys:"keys"}},fields:["key","title"]};t.default=a},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=l(n(521)),a=l(n(154));function l(e){return e&&e.__esModule?e:{default:e}}document.addEventListener("DOMContentLoaded",(()=>{(0,r.default)(),(0,a.default)()}))},521:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(648)),a=u(n(809)),l=u(n(852)),o=u(n(117)),i=u(n(606));function u(e){return e&&e.__esModule?e:{default:e}}var s=()=>{r.default.component.registerMany({LinkPicker:a.default,LinkField:l.default,"LinkModal.FormBuilderModal":o.default,"LinkModal.InsertMediaModal":i.default})};t.default=s},154:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=l(n(648)),a=l(n(689));function l(e){return e&&e.__esModule?e:{default:e}}var o=()=>{r.default.query.register("readLinkTypes",a.default)};t.default=o},852:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=g(n(363)),a=n(827),l=n(624),o=n(648),i=k(n(42)),u=k(n(809)),s=k(n(734)),d=k(n(686)),c=k(n(697)),f=g(n(123)),p=k(n(159)),y=k(n(510)),v=k(n(86)),m=k(n(754));function k(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 g(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={},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var o=a?Object.getOwnPropertyDescriptor(e,l):null;o&&(o.get||o.set)?Object.defineProperty(r,l,o):r[l]=e[l]}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:a,types:l,actions:o,isMulti:i=!1,canCreate:d}=e;const[f,v]=(0,r.useState)({}),[k,_]=(0,r.useState)(0);let g=n;Array.isArray(g)||("number"==typeof g&&0!=g&&(g=[g]),g||(g=[])),(0,r.useEffect)((()=>{if(!k&&g.length>0){const e=[];for(const t of g)e.push(`itemIDs[]=${t}`);const t=`${y.default.getSection(h).form.linkForm.dataUrl}?${e.join("&")}`;p.default.get(t).then((e=>e.json())).then((e=>{v(e)}))}}),[k,n&&n.length]);const O=()=>{_(0)},b=e=>{_(0);const t=[...g];t.includes(e)||t.push(e),a(i?t:t[0]),o.toasts.success(m.default._t("LinkField.SAVE_SUCCESS","Saved link"))},M=e=>{const t=`${y.default.getSection(h).form.linkForm.deleteUrl}/${e}`;p.default.delete(t,{},{"X-SecurityID":y.default.get("SecurityID")}).then((()=>{o.toasts.success(m.default._t("LinkField.DELETE_SUCCESS","Deleted link"))})).catch((()=>{o.toasts.error(m.default._t("LinkField.DELETE_ERROR","Failed to delete link"))}));const n={...f};delete n[e],v(n),a(i?Object.keys(n):0)},C=i||0===Object.keys(f).length,E=Boolean(k);return r.default.createElement(r.default.Fragment,null,C&&r.default.createElement(u.default,{canCreate:d,onModalSuccess:b,onModalClosed:O,types:l}),r.default.createElement("div",null," ",(()=>{const e=[];for(const d of g){var t,n,a,o,i,u;if(!f[d])continue;const c=l.hasOwnProperty(null===(t=f[d])||void 0===t?void 0:t.typeKey)?l[null===(n=f[d])||void 0===n?void 0:n.typeKey]:{};e.push(r.default.createElement(s.default,{key:d,id:d,title:null===(a=f[d])||void 0===a?void 0:a.Title,description:null===(o=f[d])||void 0===o?void 0:o.description,versionState:null===(i=f[d])||void 0===i?void 0:i.versionState,typeTitle:c.title||"",onClear:M,onClick:()=>{_(d)},canDelete:!(null===(u=f[d])||void 0===u||!u.canDelete)}))}return e})()," "),E&&r.default.createElement(c.default,{types:l,typeKey:null===(t=f[k])||void 0===t?void 0:t.typeKey,isOpen:Boolean(k),onSuccess:b,onClosed:O,linkID:k}))};O.propTypes={value:v.default.oneOfType([v.default.arrayOf(v.default.number),v.default.number]),onChange:v.default.func.isRequired,types:v.default.objectOf(d.default).isRequired,actions:v.default.object.isRequired,isMulti:v.default.bool,canCreate:v.default.bool.isRequired};var b=(0,a.compose)((0,o.injectGraphql)("readLinkTypes"),i.default,(0,l.connect)(null,(e=>({actions:{toasts:(0,a.bindActionCreators)(f,e)}}))))(O);t.default=b},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={},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var o=a?Object.getOwnPropertyDescriptor(e,l):null;o&&(o.get||o.set)?Object.defineProperty(r,l,o):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),a=s(n(475)),l=n(624),o=s(n(686)),i=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:o,onSubmit:i,...u}=e;if(!t)return!1;(0,r.useEffect)((()=>{n?o.initModal():o.reset()}),[n]);const s=l?{ID:l.FileID,Description:l.Title,TargetBlank:!!l.OpenInNew}:{};return r.default.createElement(a.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:a}=e;return i({FileID:n,Title:r,OpenInNew:a,typeKey:t.key},"",(()=>{}))}},u))};c.propTypes={type:o.default.isRequired,editing:i.default.bool.isRequired,data:i.default.object.isRequired,actions:i.default.object.isRequired,onClick:i.default.func.isRequired};var f=(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"})}}}))(c);t.default=f},117:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=s(n(363)),a=s(n(912)),l=s(n(872)),o=s(n(902)),i=s(n(510)),u=s(n(86));function s(e){return e&&e.__esModule?e:{default:e}}const d=(e,t)=>{const{schemaUrl:n}=i.default.getSection("SilverStripe\\LinkField\\Controllers\\LinkFieldController").form.linkForm,r=l.default.parse(n),a=o.default.parse(r.query);a.typeKey=e;for(const e of["href","path","pathname"])r[e]=`${r[e]}/${t}`;return l.default.format({...r,search:o.default.stringify(a)})},c=e=>{let{typeTitle:t,typeKey:n,linkID:l=0,isOpen:o,onSuccess:i,onClosed:u}=e;if(!n)return!1;return r.default.createElement(a.default,{title:t,isOpen:o,schemaUrl:d(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);i(t)}return Promise.resolve()},onClosed:u})};c.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 f=c;t.default=f},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={},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var o=a?Object.getOwnPropertyDescriptor(e,l):null;o&&(o.get||o.set)?Object.defineProperty(r,l,o):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),a=s(n(86)),l=s(n(820)),o=s(n(97)),i=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 c=e=>{let{types:t,onModalSuccess:n,onModalClosed:a,canCreate:i}=e;const[s,d]=(0,r.useState)(""),c=""!==s,f=(0,l.default)("link-picker","form-control"),p=Object.values(t);return i?r.default.createElement("div",{className:f},r.default.createElement(o.default,{types:p,onSelect:e=>{d(e)},canCreate:i}),c&&r.default.createElement(u.default,{types:t,typeKey:s,isOpen:c,onSuccess:e=>{d(""),n(e)},onClosed:()=>{"function"==typeof a&&a(),d("")}})):r.default.createElement("div",{className:f},r.default.createElement("div",{className:"link-picker__cannot-create"},"Cannot create link"))};t.Component=c,c.propTypes={types:a.default.objectOf(i.default).isRequired,onModalSuccess:a.default.func.isRequired,onModalClosed:a.default.func,canCreate:a.default.bool.isRequired};var f=c;t.default=f},97:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=s(n(754)),a=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={},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var o=a?Object.getOwnPropertyDescriptor(e,l):null;o&&(o.get||o.set)?Object.defineProperty(r,l,o):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),l=s(n(86)),o=n(127),i=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,canCreate:l}=e;const[i,u]=(0,a.useState)(!1);return a.default.createElement(a.default.Fragment,null,l&&a.default.createElement(o.Dropdown,{isOpen:i,toggle:()=>u((e=>!e)),className:"link-picker__menu"},a.default.createElement(o.DropdownToggle,{className:"link-picker__menu-toggle font-icon-plus-1",caret:!0},r.default._t("LinkField.ADD_LINK","Add Link")),a.default.createElement(o.DropdownMenu,null,t.map((e=>{let{key:t,title:r}=e;return a.default.createElement(o.DropdownItem,{key:t,onClick:()=>n(t)},r)})))),!l&&a.default.createElement("div",{className:"link-picker__cannot-create"},"Cannot create link"))};d.propTypes={types:l.default.arrayOf(i.default).isRequired,onSelect:l.default.func.isRequired,canCreate:l.default.bool.isRequired};var c=d;t.default=c},734:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(820)),a=u(n(754)),l=u(n(363)),o=u(n(86)),i=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:o,versionState:u,typeTitle:d,onClear:c,onClick:f,canDelete:p}=e;const y={"link-picker__link":!0,"form-control":!0};u&&(y[` link-picker__link--${u}`]=!0),n&&n.length>25&&(n=n.substring(0,25)+"...");const v=(0,r.default)(y);return l.default.createElement("div",{className:v},l.default.createElement(i.Button,{className:"link-picker__button font-icon-link",color:"secondary",onClick:s(f)},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=a.default._t("LinkField.LINK_DRAFT_TITLE","Link has draft changes"),n=a.default._t("LinkField.LINK_DRAFT_LABEL","Draft");else{if("modified"!==e)return null;t=a.default._t("LinkField.LINK_MODIFIED_TITLE","Link has unpublished changes"),n=a.default._t("LinkField.LINK_MODIFIED_LABEL","Modified")}const o=(0,r.default)("badge",`status-${e}`);return l.default.createElement("span",{className:o,title:t},n)})(u)),l.default.createElement("small",{className:"link-picker__type"},d,": ",l.default.createElement("span",{className:"link-picker__url"},o)))),p&&l.default.createElement(i.Button,{className:"link-picker__clear",color:"link",onClick:s((()=>c(t)))},a.default._t("LinkField.CLEAR","Clear")))};d.propTypes={id:o.default.number.isRequired,title:o.default.string,description:o.default.string,versionState:o.default.string,typeTitle:o.default.string.isRequired,onClear:o.default.func.isRequired,onClick:o.default.func.isRequired,canDelete:o.default.bool.isRequired};var c=d;t.default=c},697:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=i(n(363)),a=n(648),l=i(n(86)),o=i(n(686));function i(e){return e&&e.__esModule?e:{default:e}}const u=e=>{let{types:t,typeKey:n,linkID:l=0,isOpen:o,onSuccess:i,onClosed:u}=e;if(!n)return!1;const s=t.hasOwnProperty(n)?t[n]:{},d=s&&s.hasOwnProperty("handlerName")?s.handlerName:"FormBuilderModal",c=(0,a.loadComponent)(`LinkModal.${d}`);return r.default.createElement(c,{typeTitle:s.title||"",typeKey:n,linkID:l,isOpen:o,onSuccess:i,onClosed:u})};u.propTypes={types:l.default.objectOf(o.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=i(n(311)),a=i(n(363)),l=i(n(691)),o=n(648);function i(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,o.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(a.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,canCreate:this.getInputField().data("can-create")??!1}},getInputField(){const t=this.data("field-id");return e(`#${t}`)},onunmatch(){const e=this.getRoot();e&&e.unmount()}})}))},689:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n(648);const a={props(e){const{data:{error:t,readLinkTypes:n,loading:r}}=e,a=t&&t.graphQLErrors&&t.graphQLErrors.map((e=>e.message));return{loading:r,types:n?n.reduce(((e,t)=>({...e,[t.key]:t})),{}):{},graphQLErrors:a}}},{READ:l}=r.graphqlTemplates;var o={apolloConfig:a,templateName:l,pluralName:"LinkTypes",pagination:!1,params:{keys:"[ID]"},args:{root:{keys:"keys"}},fields:["key","title"]};t.default=o},686:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,a=(r=n(86))&&r.__esModule?r:{default:r};var l=a.default.shape({key:a.default.string.isRequired,title:a.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 a=t[r];if(void 0!==a)return a.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/dist/styles/bundle.css b/client/dist/styles/bundle.css index 4abdf5cf..e466a480 100644 --- a/client/dist/styles/bundle.css +++ b/client/dist/styles/bundle.css @@ -1 +1 @@ -.link-picker__link,.link-picker{display:flex;height:auto;width:100%;min-height:54px;background:#fff;padding:0}.link-picker{align-items:stretch;cursor:pointer;box-shadow:none}.link-picker:not(:last-child){margin-bottom:10px}.link-picker.font-icon-link::before{margin:.76925rem}.link-picker__menu{flex-grow:1}.link-picker__menu-toggle{width:100%;height:100%;text-align:left}.link-picker__menu-toggle::before{padding:.76925rem}.link-picker__link{align-items:center;text-align:left;margin-right:0;justify-content:space-between;position:relative}.link-picker__link:not(:last-child){border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.link-picker__link:not(:first-child){border-top:0;border-top-left-radius:0;border-top-right-radius:0}.link-picker__link:hover,.link-picker__link:focus{background:#eef0f4;text-decoration:none;color:inherit}.link-picker__link::before{top:29px;left:32px;content:" ";position:absolute;border:1px solid #cf3f00;border-radius:100%;bottom:6px;box-shadow:0 0 1px .5px #fff;display:block;height:8px;width:8px;z-index:1}.link-picker__link--draft::before{background-color:#ff7f22}.link-picker__link--modified::before{background-color:#fff7f0}.link-picker__link--unsaved::before,.link-picker__link--published::before{display:none}.link-picker__button{display:flex;align-items:center;flex-grow:1;height:100%;text-align:left;border:none;margin-right:0}.link-picker__button::before{font-size:1.231rem;padding:.76925rem;margin-right:6px;flex-grow:0}.link-picker__link-detail{flex-grow:1}.link-picker__clear{flex-grow:0}.link-picker__url{color:#0071c4}.link-picker__title-text{margin-right:5px}.link-picker__title .badge{color:#cf3f00;background-color:#fff2ea;padding:2px 3px 2px 4px} +.link-picker__link,.link-picker{display:flex;height:auto;width:100%;min-height:54px;background:#fff;padding:0}.link-picker{align-items:stretch;cursor:pointer;box-shadow:none}.link-picker:not(:last-child){margin-bottom:10px}.link-picker.font-icon-link::before{margin:.76925rem}.link-picker__cannot-create{cursor:default;flex-grow:1;padding:16px 13px}.link-picker__menu{flex-grow:1}.link-picker__menu-toggle{width:100%;height:100%;text-align:left}.link-picker__menu-toggle::before{padding:.76925rem}.link-picker__link{align-items:center;text-align:left;margin-right:0;justify-content:space-between;position:relative}.link-picker__link:not(:last-child){border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0}.link-picker__link:not(:first-child){border-top:0;border-top-left-radius:0;border-top-right-radius:0}.link-picker__link:hover,.link-picker__link:focus{background:#eef0f4;text-decoration:none;color:inherit}.link-picker__link::before{top:29px;left:32px;content:" ";position:absolute;border:1px solid #cf3f00;border-radius:100%;bottom:6px;box-shadow:0 0 1px .5px #fff;display:block;height:8px;width:8px;z-index:1}.link-picker__link--draft::before{background-color:#ff7f22}.link-picker__link--modified::before{background-color:#fff7f0}.link-picker__link--unsaved::before,.link-picker__link--published::before{display:none}.link-picker__button{display:flex;align-items:center;flex-grow:1;height:100%;text-align:left;border:none;margin-right:0}.link-picker__button::before{font-size:1.231rem;padding:.76925rem;margin-right:6px;flex-grow:0}.link-picker__link-detail{flex-grow:1}.link-picker__clear{flex-grow:0}.link-picker__url{color:#0071c4}.link-picker__title-text{margin-right:5px}.link-picker__title .badge{color:#cf3f00;background-color:#fff2ea;padding:2px 3px 2px 4px} diff --git a/client/src/components/LinkField/LinkField.js b/client/src/components/LinkField/LinkField.js index a336a9a8..ba3a9c4d 100644 --- a/client/src/components/LinkField/LinkField.js +++ b/client/src/components/LinkField/LinkField.js @@ -24,7 +24,7 @@ const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController'; * actions - object of redux actions * isMulti - whether this field handles multiple links or not */ -const LinkField = ({ value = null, onChange, types, actions, isMulti = false }) => { +const LinkField = ({ value = null, onChange, types, actions, isMulti = false, canCreate }) => { const [data, setData] = useState({}); const [editingID, setEditingID] = useState(0); @@ -145,6 +145,7 @@ const LinkField = ({ value = null, onChange, types, actions, isMulti = false }) typeTitle={type.title || ''} onClear={onClear} onClick={() => { setEditingID(linkID); }} + canDelete={data[linkID]?.canDelete ? true : false} />); } return links; @@ -154,7 +155,7 @@ const LinkField = ({ value = null, onChange, types, actions, isMulti = false }) const renderModal = Boolean(editingID); return <> - { renderPicker && } + { renderPicker && }
{ renderLinks() }
{ renderModal && { +const LinkPicker = ({ types, onModalSuccess, onModalClosed, canCreate }) => { const [typeKey, setTypeKey] = useState(''); /** @@ -41,9 +41,17 @@ const LinkPicker = ({ types, onModalSuccess, onModalClosed }) => { const className = classnames('link-picker', 'form-control'); const typeArray = Object.values(types); + if (!canCreate) { + return ( +
+
Cannot create link
+
+ ); + } + return (
- + { shouldOpenModal && { +const LinkPickerMenu = ({ types, onSelect, canCreate }) => { const [isOpen, setIsOpen] = useState(false); const toggle = () => setIsOpen(prevState => !prevState); - return ( - - {i18n._t('LinkField.ADD_LINK', 'Add Link')} - - {types.map(({key, title}) => - onSelect(key)}>{title} - )} - - - ); + return <> + { canCreate && + + {i18n._t('LinkField.ADD_LINK', 'Add Link')} + + {types.map(({key, title}) => + onSelect(key)}>{title} + )} + + + } + { !canCreate && +
Cannot create link
+ } + ; }; LinkPickerMenu.propTypes = { types: PropTypes.arrayOf(LinkType).isRequired, - onSelect: PropTypes.func.isRequired + onSelect: PropTypes.func.isRequired, + canCreate: PropTypes.bool.isRequired, }; export default LinkPickerMenu; diff --git a/client/src/components/LinkPicker/LinkPickerTitle.js b/client/src/components/LinkPicker/LinkPickerTitle.js index ee9852f3..a2d3a290 100644 --- a/client/src/components/LinkPicker/LinkPickerTitle.js +++ b/client/src/components/LinkPicker/LinkPickerTitle.js @@ -29,7 +29,16 @@ const getVersionedBadge = (versionState) => { return {label}; }; -const LinkPickerTitle = ({ id, title, description, versionState, typeTitle, onClear, onClick }) => { +const LinkPickerTitle = ({ + id, + title, + description, + versionState, + typeTitle, + onClear, + onClick, + canDelete +}) => { const classes = { 'link-picker__link': true, 'form-control': true, @@ -54,7 +63,9 @@ const LinkPickerTitle = ({ id, title, description, versionState, typeTitle, onCl
- + {canDelete && + + } }; @@ -66,6 +77,7 @@ LinkPickerTitle.propTypes = { typeTitle: PropTypes.string.isRequired, onClear: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired, + canDelete: PropTypes.bool.isRequired, }; export default LinkPickerTitle; diff --git a/client/src/entwine/LinkField.js b/client/src/entwine/LinkField.js index 0c8b789b..b23aab08 100644 --- a/client/src/entwine/LinkField.js +++ b/client/src/entwine/LinkField.js @@ -50,6 +50,7 @@ jQuery.entwine('ss', ($) => { value, onChange: this.handleChange.bind(this), isMulti: this.data('is-multi') ?? false, + canCreate: this.getInputField().data('can-create') ?? false, }; }, diff --git a/src/Controllers/LinkFieldController.php b/src/Controllers/LinkFieldController.php index 5340a077..2195f6fa 100644 --- a/src/Controllers/LinkFieldController.php +++ b/src/Controllers/LinkFieldController.php @@ -112,6 +112,7 @@ private function getLinkData(Link $link): array $this->jsonError(403, _t('LinkField.UNAUTHORIZED', 'Unauthorized')); } $data = $link->jsonSerialize(); + $data['canDelete'] = $link->canDelete(); $data['description'] = $link->getDescription(); $data['versionState'] = $link->getVersionedState(); return $data; diff --git a/src/Form/LinkField.php b/src/Form/LinkField.php index e02a3d98..4a91fe8b 100644 --- a/src/Form/LinkField.php +++ b/src/Form/LinkField.php @@ -57,10 +57,18 @@ public function saveInto(DataObjectInterface $record) return $this; } + public function getSchemaStateDefaults() + { + $data = parent::getSchemaStateDefaults(); + $data['canCreate'] = !$this->isReadonly(); + return $data; + } + protected function getDefaultAttributes(): array { $attributes = parent::getDefaultAttributes(); $attributes['data-value'] = $this->Value(); + $attributes['data-can-create'] = !$this->isReadonly(); return $attributes; } } diff --git a/src/Form/MultiLinkField.php b/src/Form/MultiLinkField.php index f4874b95..5119c970 100644 --- a/src/Form/MultiLinkField.php +++ b/src/Form/MultiLinkField.php @@ -10,6 +10,7 @@ use SilverStripe\ORM\RelationList; use SilverStripe\ORM\SS_List; use SilverStripe\ORM\UnsavedRelationList; +use SilverStripe\LinkField\Models\Link; /** * Allows CMS users to edit a Link object. @@ -68,6 +69,7 @@ public function getSchemaStateDefaults() { $data = parent::getSchemaStateDefaults(); $data['value'] = $this->getValueArray(); + $data['canCreate'] = !$this->isReadonly(); return $data; } @@ -75,6 +77,7 @@ protected function getDefaultAttributes(): array { $attributes = parent::getDefaultAttributes(); $attributes['data-value'] = $this->getValueArray(); + $attributes['data-can-create'] = !$this->isReadonly(); return $attributes; } diff --git a/src/Models/FileLink.php b/src/Models/FileLink.php index a0b5051e..9e2afb30 100644 --- a/src/Models/FileLink.php +++ b/src/Models/FileLink.php @@ -30,7 +30,11 @@ public function getCMSFields(): FieldList public function getDescription(): string { - return $this->File()?->getFilename() ?? ''; + $file = $this->File(); + if (!$file?->exists() || !$file->canView()) { + return ''; + } + return $file->getFilename() ?? ''; } public function getURL(): string diff --git a/src/Models/SiteTreeLink.php b/src/Models/SiteTreeLink.php index 9dcf9a90..88a00f00 100644 --- a/src/Models/SiteTreeLink.php +++ b/src/Models/SiteTreeLink.php @@ -32,7 +32,11 @@ class SiteTreeLink extends Link public function getDescription(): string { - return $this->Page()?->URLSegment ?? ''; + $page = $this->Page(); + if (!$page->exists() || !$page->canView()) { + return ''; + } + return $page->URLSegment ?? ''; } public function getCMSFields(): FieldList @@ -113,15 +117,15 @@ public function getURL(): string public function getDefaultTitle(): string { $page = $this->Page(); - $pageExist = $this->Page()->exists(); - - if (!$pageExist) { + if (!$page->exists()) { return _t( static::class . '.MISSING_DEFAULT_TITLE', 'Page missing', ); } - + if (!$page->canView()) { + return ''; + } return $page->Title; } } diff --git a/templates/SilverStripe/LinkField/Form/MultiLinkField.ss b/templates/SilverStripe/LinkField/Form/MultiLinkField.ss index 6a3ef5d7..af8a6b0d 100644 --- a/templates/SilverStripe/LinkField/Form/MultiLinkField.ss +++ b/templates/SilverStripe/LinkField/Form/MultiLinkField.ss @@ -1,2 +1,2 @@ -
+
diff --git a/tests/php/Controllers/LinkFieldControllerTest.php b/tests/php/Controllers/LinkFieldControllerTest.php index 03300a3c..fee2912f 100644 --- a/tests/php/Controllers/LinkFieldControllerTest.php +++ b/tests/php/Controllers/LinkFieldControllerTest.php @@ -7,6 +7,7 @@ use SilverStripe\LinkField\Type\Registry; use SilverStripe\Core\Config\Config; use SilverStripe\Security\SecurityToken; +use SilverStripe\Control\HTTPRequest; class LinkFieldControllerTest extends FunctionalTest { @@ -230,15 +231,15 @@ public function provideLinkFormPost(): array // note: not duplicating code paths already tested with provideLinkFormGetSchema() // e.g. Reject Invalid ID return [ - // 'Valid update existing record' => [ - // 'idType' => 'existing', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => '', - // 'expectedCode' => 200, - // 'expectedMessage' => '', - // 'expectedLinkType' => 'existing', - // ], + 'Valid update existing record' => [ + 'idType' => 'existing', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => '', + 'expectedCode' => 200, + 'expectedMessage' => '', + 'expectedLinkType' => 'existing', + ], 'Valid create new record' => [ 'idType' => 'new-record', 'typeKey' => 'testphone', @@ -248,105 +249,147 @@ public function provideLinkFormPost(): array 'expectedMessage' => '', 'expectedLinkType' => 'new-record', ], - // 'Invalid validate()' => [ - // 'idType' => 'existing', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => 'validate', - // 'expectedCode' => 200, - // 'expectedMessage' => 'Fail was validate', - // 'expectedLinkType' => 'existing', - // ], - // 'Invalid getCMSCompositeValidator()' => [ - // 'idType' => 'existing', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => 'cms-composite-validator', - // 'expectedCode' => 200, - // 'expectedMessage' => 'Fail was cms-composite-validator', - // 'expectedLinkType' => 'existing', - // ], - // 'Reject invalid ID' => [ - // 'idType' => 'invalid', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => '', - // 'expectedCode' => 404, - // 'expectedMessage' => 'Invalid ID', - // 'expectedLinkType' => '', - // ], - // 'Reject missing ID' => [ - // 'idType' => 'missing', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => '', - // 'expectedCode' => 404, - // 'expectedMessage' => 'Invalid ID', - // 'expectedLinkType' => '', - // ], - // 'Reject non-numeric ID' => [ - // 'idType' => 'non-numeric', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => '', - // 'expectedCode' => 404, - // 'expectedMessage' => 'Invalid ID', - // 'expectedLinkType' => '', - // ], - // 'Reject invalid typeKey for new record' => [ - // 'idType' => 'new-record', - // 'typeKey' => 'donut', - // 'dataType' => 'valid', - // 'fail' => '', - // 'expectedCode' => 404, - // 'expectedMessage' => 'Invalid typeKey', - // 'expectedLinkType' => '', - // ], - // 'Reject empty data' => [ - // 'idType' => 'existing', - // 'typeKey' => 'testphone', - // 'dataType' => 'empty', - // 'fail' => '', - // 'expectedCode' => 400, - // 'expectedMessage' => 'Empty data', - // 'expectedLinkType' => '', - // ], - // 'Reject invalid-id data' => [ - // 'idType' => 'existing', - // 'typeKey' => 'testphone', - // 'dataType' => 'invalid-id', - // 'fail' => '', - // 'expectedCode' => 400, - // 'expectedMessage' => 'Bad data', - // 'expectedLinkType' => '', - // ], - // 'Reject fail csrf-token' => [ - // 'idType' => 'existing', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => 'csrf-token', - // 'expectedCode' => 400, - // 'expectedMessage' => 'Invalid CSRF token', - // 'expectedLinkType' => '', - // ], - // 'Reject fail canEdit() check existing record' => [ - // 'idType' => 'existing', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => 'can-edit', - // 'expectedCode' => 403, - // 'expectedMessage' => 'Unauthorized', - // 'expectedLinkType' => '', - // ], - // 'Reject fail canCreate() check new record' => [ - // 'idType' => 'new-record', - // 'typeKey' => 'testphone', - // 'dataType' => 'valid', - // 'fail' => 'can-create', - // 'expectedCode' => 403, - // 'expectedMessage' => 'Unauthorized', - // 'expectedLinkType' => '', - // ], + 'Invalid validate()' => [ + 'idType' => 'existing', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => 'validate', + 'expectedCode' => 200, + 'expectedMessage' => 'Fail was validate', + 'expectedLinkType' => 'existing', + ], + 'Invalid getCMSCompositeValidator()' => [ + 'idType' => 'existing', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => 'cms-composite-validator', + 'expectedCode' => 200, + 'expectedMessage' => 'Fail was cms-composite-validator', + 'expectedLinkType' => 'existing', + ], + 'Reject invalid ID' => [ + 'idType' => 'invalid', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => '', + 'expectedCode' => 404, + 'expectedMessage' => 'Invalid ID', + 'expectedLinkType' => '', + ], + 'Reject missing ID' => [ + 'idType' => 'missing', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => '', + 'expectedCode' => 404, + 'expectedMessage' => 'Invalid ID', + 'expectedLinkType' => '', + ], + 'Reject non-numeric ID' => [ + 'idType' => 'non-numeric', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => '', + 'expectedCode' => 404, + 'expectedMessage' => 'Invalid ID', + 'expectedLinkType' => '', + ], + 'Reject invalid typeKey for new record' => [ + 'idType' => 'new-record', + 'typeKey' => 'donut', + 'dataType' => 'valid', + 'fail' => '', + 'expectedCode' => 404, + 'expectedMessage' => 'Invalid typeKey', + 'expectedLinkType' => '', + ], + 'Reject empty data' => [ + 'idType' => 'existing', + 'typeKey' => 'testphone', + 'dataType' => 'empty', + 'fail' => '', + 'expectedCode' => 400, + 'expectedMessage' => 'Empty data', + 'expectedLinkType' => '', + ], + 'Reject invalid-id data' => [ + 'idType' => 'existing', + 'typeKey' => 'testphone', + 'dataType' => 'invalid-id', + 'fail' => '', + 'expectedCode' => 400, + 'expectedMessage' => 'Bad data', + 'expectedLinkType' => '', + ], + 'Reject fail csrf-token' => [ + 'idType' => 'existing', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => 'csrf-token', + 'expectedCode' => 400, + 'expectedMessage' => 'Invalid CSRF token', + 'expectedLinkType' => '', + ], + 'Reject fail canEdit() check existing record' => [ + 'idType' => 'existing', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => 'can-edit', + 'expectedCode' => 403, + 'expectedMessage' => 'Unauthorized', + 'expectedLinkType' => '', + ], + 'Reject fail canCreate() check new record' => [ + 'idType' => 'new-record', + 'typeKey' => 'testphone', + 'dataType' => 'valid', + 'fail' => 'can-create', + 'expectedCode' => 403, + 'expectedMessage' => 'Unauthorized', + 'expectedLinkType' => '', + ], + ]; + } + + /** + * @dataProvider provideLinkFormReadOnly + */ + public function testLinkFormReadonly(string $idType, string $fail, bool $expected): void + { + TestPhoneLink::$fail = $fail; + $id = $this->getID($idType); + $typeKey = 'testphone'; + $url = "/admin/linkfield/schema/linkForm/$id?typeKey=$typeKey"; + $headers = $this->formSchemaHeader(); + $body = $this->get($url, null, $headers)->getBody(); + $json = json_decode($body, true); + $actual = $json['schema']['fields'][0]['children'][0]['readOnly'] ?? false; + $this->assertSame($expected, $actual); + } + + public function provideLinkFormReadOnly(): array + { + return [ + [ + 'idType' => 'existing', + 'fail' => '', + "expected" => false, + ], + [ + 'idType' => 'existing', + 'fail' => 'can-edit', + "expected" => true, + ], + [ + 'idType' => 'new-record', + 'fail' => '', + "expected" => false, + ], + [ + 'idType' => 'new-record', + 'fail' => 'can-create', + "expected" => true, + ], ]; } diff --git a/tests/php/Models/FileLinkTest.php b/tests/php/Models/FileLinkTest.php new file mode 100644 index 00000000..8fa69263 --- /dev/null +++ b/tests/php/Models/FileLinkTest.php @@ -0,0 +1,38 @@ +assertSame('', $link->getDescription()); + // FileLink with a page though cannot view the page + $file = new TestFileCannotView(['Name' => 'not-allowed']); + $file->setFromLocalFile(realpath(__DIR__ .'/FileLinkTest/file-a.png'), 'file-a.png'); + $file->write(); + $link->File = $file->ID; + $link->write(); + $this->assertSame('', $link->getDescription()); + // FileLink with a page that and can view the page + $file = new TestFileCanView(['Name' => 'allowed']); + $file->setFromLocalFile(realpath(__DIR__ .'/FileLinkTest/file-b.png'), 'file-b.png'); + $file->write(); + $link->File = $file->ID; + $link->write(); + $this->assertSame('file-b.png', $link->getDescription()); + } +} diff --git a/tests/php/Models/FileLinkTest/TestFileCanView.php b/tests/php/Models/FileLinkTest/TestFileCanView.php new file mode 100644 index 00000000..50539538 --- /dev/null +++ b/tests/php/Models/FileLinkTest/TestFileCanView.php @@ -0,0 +1,14 @@ +KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0003tNkluLCd9Bbz-8o7tzI(0EC6`UF9HrzE$S<#` zJ9ikQs5>jK71}-rculVcr7g2U+InyAA5j#Mt)i4(u6$*eR^Fg=9LHIfiLI)tN(iy{ zJO`!IG=(?HaWY#~-tK$%2c9uzwy0w~IZpFDFTWHa1Y-=($9S;0AF(7!5JH@DA0A^Y z2m;2~W5uDD=Q%=%Qu@4ju!k`wge=Qqj0wZ=(Kc5e=NusfAINjt9G)li?4@mP`$s8# zX`7Gl#g)ex^XCC+iOF$-D`M~c*Yv8q-8XZ&@|B_FAK&j@=NBiQ?|IPoynFtyoSs!a Z2LOC}I*2oeDAE7`002ovPDHLkV1nE|yGH;3 literal 0 HcmV?d00001 diff --git a/tests/php/Models/FileLinkTest/file-b.png b/tests/php/Models/FileLinkTest/file-b.png new file mode 100644 index 0000000000000000000000000000000000000000..07e62c1cc239d810ddc965f43a4761874b4afacf GIT binary patch literal 3083 zcmV+m4D|DfP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0003tNkluLCd9Bbz-8o7tzI(0EC6`UF9HrzE$S<#` zJ9ikQs5>jK71}-rculVcr7g2U+InyAA5j#Mt)i4(u6$*eR^Fg=9LHIfiLI)tN(iy{ zJO`!IG=(?HaWY#~-tK$%2c9uzwy0w~IZpFDFTWHa1Y-=($9S;0AF(7!5JH@DA0A^Y z2m;2~W5uDD=Q%=%Qu@4ju!k`wge=Qqj0wZ=(Kc5e=NusfAINjt9G)li?4@mP`$s8# zX`7Gl#g)ex^XCC+iOF$-D`M~c*Yv8q-8XZ&@|B_FAK&j@=NBiQ?|IPoynFtyoSs!a Z2LOC}I*2oeDAE7`002ovPDHLkV1nE|yGH;3 literal 0 HcmV?d00001 diff --git a/tests/php/Models/SiteTreeLinkTest.php b/tests/php/Models/SiteTreeLinkTest.php new file mode 100644 index 00000000..b9c26383 --- /dev/null +++ b/tests/php/Models/SiteTreeLinkTest.php @@ -0,0 +1,48 @@ +assertSame('', $link->getDescription()); + // SiteTreeLink with a page though cannot view the page + $page = new TestSiteTreeCannotView(['URLSegment' => 'test-a']); + $page->write(); + $link->Page = $page->ID; + $link->write(); + $this->assertSame('', $link->getDescription()); + // SiteTreeLink with a page that and can view the page + $page = new TestSiteTreeCanView(['URLSegment' => 'test-b']); + $page->write(); + $link->Page = $page->ID; + $link->write(); + $this->assertSame('test-b', $link->getDescription()); + } + + public function testGetDefaultTitle(): void + { + // Page does not exist + $link = SiteTreeLink::create(); + $this->assertSame('Page missing', $link->getDefaultTitle()); + // Page exists + $page = new TestSiteTreeCanView(['Title' => 'My test page']); + $page->write(); + $link->Page = $page->ID; + $link->write(); + $this->assertSame('My test page', $link->getDefaultTitle()); + } +} diff --git a/tests/php/Models/SiteTreeLinkTest/TestSiteTreeCanView.php b/tests/php/Models/SiteTreeLinkTest/TestSiteTreeCanView.php new file mode 100644 index 00000000..c8ab74ab --- /dev/null +++ b/tests/php/Models/SiteTreeLinkTest/TestSiteTreeCanView.php @@ -0,0 +1,14 @@ +