diff --git a/README.md b/README.md index 7182a1da..d6459eca 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,22 @@ class Page extends SiteTree ]; private static $has_many = [ - 'HasManyLinks' => Link::class + 'HasManyLinks' => Link::class, + ]; + + private static array $owns = [ + 'HasOneLink', + 'HasManyLinks', + ]; + + private static array $cascade_deletess = [ + 'HasOneLink', + 'HasManyLinks', + ]; + + private static array $cascade_duplicatess = [ + 'HasOneLink', + 'HasManyLinks', ]; public function getCMSFields() diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index d63e64dd..2d44058e 100644 --- a/client/dist/js/bundle.js +++ b/client/dist/js/bundle.js @@ -1 +1,1024 @@ -!function(){"use strict";var e={274:function(e,t,n){var r=l(n(521)),o=l(n(154));function l(e){return e&&e.__esModule?e:{default:e}}document.addEventListener("DOMContentLoaded",(()=>{(0,r.default)(),(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)),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:o.default,LinkField:l.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=l(n(648)),o=l(n(689));function l(e){return e&&e.__esModule?e:{default:e}}var a=()=>{r.default.query.register("readLinkTypes",o.default)};t.default=a},852:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=g(n(363)),o=n(827),l=n(624),a=n(648),i=k(n(42)),u=k(n(809)),s=k(n(734)),d=k(n(686)),f=k(n(697)),c=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={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var a=o?Object.getOwnPropertyDescriptor(e,l):null;a&&(a.get||a.set)?Object.defineProperty(r,l,a):r[l]=e[l]}return r.default=e,n&&n.set(e,r),r}const O="SilverStripe\\LinkField\\Controllers\\LinkFieldController",h=e=>{var t;let{value:n=null,onChange:o,types:l,actions:a,isMulti:i=!1}=e;const[d,c]=(0,r.useState)({}),[v,k]=(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(O).form.linkForm.dataUrl}?${e.join("&")}`;p.default.get(t).then((e=>e.json())).then((e=>{c(e)}))}}),[v,n&&n.length]);const g=()=>{k(0)},h=e=>{k(0);const t=[..._];t.includes(e)||t.push(e),o(i?t:t[0]),a.toasts.success(m.default._t("LinkField.SAVE_SUCCESS","Saved link"))},b=e=>{const t=`${y.default.getSection(O).form.linkForm.deleteUrl}/${e}`;p.default.delete(t,{},{"X-SecurityID":y.default.get("SecurityID")}).then((()=>{a.toasts.success(m.default._t("LinkField.DELETE_SUCCESS","Deleted link"))})).catch((()=>{a.toasts.error(m.default._t("LinkField.DELETE_ERROR","Failed to delete link"))}));const n={...d};delete n[e],c(n),o(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:h,onModalClosed:g,types:l}),r.default.createElement("div",null," ",(()=>{const e=[];for(const i of _){var t,n,o,a;if(!d[i])continue;const u=l.hasOwnProperty(null===(t=d[i])||void 0===t?void 0:t.typeKey)?l[null===(n=d[i])||void 0===n?void 0:n.typeKey]:{};e.push(r.default.createElement(s.default,{key:i,id:i,title:null===(o=d[i])||void 0===o?void 0:o.Title,description:null===(a=d[i])||void 0===a?void 0:a.description,typeTitle:u.title||"",onClear:b,onClick:()=>{k(i)}}))}return e})()," "),j&&r.default.createElement(f.default,{types:l,typeKey:null===(t=d[v])||void 0===t?void 0:t.typeKey,isOpen:Boolean(v),onSuccess:h,onClosed:g,linkID:v}))};h.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,o.compose)((0,a.injectGraphql)("readLinkTypes"),i.default,(0,l.connect)(null,(e=>({actions:{toasts:(0,o.bindActionCreators)(c,e)}}))))(h);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={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var a=o?Object.getOwnPropertyDescriptor(e,l):null;a&&(a.get||a.set)?Object.defineProperty(r,l,a):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),o=s(n(475)),l=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:l,actions:a,onSubmit:i,...u}=e;if(!t)return!1;(0,r.useEffect)((()=>{n?a.initModal():a.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 i({FileID:n,Title:r,OpenInNew:o,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,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=s(n(363)),o=s(n(912)),l=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=l.default.parse(n),o=a.default.parse(r.query);o.typeKey=e;for(const e of["href","path","pathname"])r[e]=`${r[e]}/${t}`;return l.default.format({...r,search:a.default.stringify(o)})},f=e=>{let{typeTitle:t,typeKey:n,linkID:l=0,isOpen:a,onSuccess:i,onClosed:u}=e;if(!n)return!1;return r.default.createElement(o.default,{title:t,isOpen:a,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})};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={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var l in e)if("default"!==l&&Object.prototype.hasOwnProperty.call(e,l)){var a=o?Object.getOwnPropertyDescriptor(e,l):null;a&&(a.get||a.set)?Object.defineProperty(r,l,a):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),o=s(n(86)),l=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:o}=e;const[i,s]=(0,r.useState)(""),d=""!==i,f=(0,l.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 o&&o(),s("")}}))};t.Component=f,f.propTypes={types:o.default.objectOf(i.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 a=o?Object.getOwnPropertyDescriptor(e,l):null;a&&(a.get||a.set)?Object.defineProperty(r,l,a):r[l]=e[l]}r.default=e,n&&n.set(e,r);return r}(n(363)),l=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[l,i]=(0,o.useState)(!1);return o.default.createElement(a.Dropdown,{isOpen:l,toggle:()=>i((e=>!e)),className:"link-picker__menu"},o.default.createElement(a.DropdownToggle,{className:"link-picker__menu-toggle font-icon-plus-1",caret:!0},r.default._t("LinkField.ADD_LINK","Add Link")),o.default.createElement(a.DropdownMenu,null,t.map((e=>{let{key:t,title:r}=e;return o.default.createElement(a.DropdownItem,{key:t,onClick:()=>n(t)},r)}))))};d.propTypes={types:l.default.arrayOf(i.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=i(n(754)),o=i(n(363)),l=i(n(86)),a=n(127);function i(e){return e&&e.__esModule?e:{default:e}}const u=e=>t=>{t.nativeEvent.stopImmediatePropagation(),t.preventDefault(),t.nativeEvent.preventDefault(),t.stopPropagation(),e&&e()},s=e=>{let{id:t,title:n,description:l,typeTitle:i,onClear:s,onClick:d}=e;return o.default.createElement("div",{className:classnames("link-picker__link","form-control")},o.default.createElement(a.Button,{className:"link-picker__button font-icon-link",color:"secondary",onClick:u(d)},o.default.createElement("div",{className:"link-picker__link-detail"},o.default.createElement("div",{className:"link-picker__title"},n),o.default.createElement("small",{className:"link-picker__type"},i,": ",o.default.createElement("span",{className:"link-picker__url"},l)))),o.default.createElement(a.Button,{className:"link-picker__clear",color:"link",onClick:u((()=>s(t)))},r.default._t("LinkField.CLEAR","Clear")))};s.propTypes={id:l.default.number.isRequired,title:l.default.string,description:l.default.string,typeTitle:l.default.string.isRequired,onClear:l.default.func.isRequired,onClick:l.default.func.isRequired};var d=s;t.default=d},697:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=i(n(363)),o=n(648),l=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:l=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,o.loadComponent)(`LinkModal.${d}`);return r.default.createElement(f,{typeTitle:s.title||"",typeKey:n,linkID:l,isOpen:a,onSuccess:i,onClosed:u})};u.propTypes={types:l.default.objectOf(a.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)),o=i(n(363)),l=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(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(){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 o={props(e){const{data:{error:t,readLinkTypes:n,loading:r}}=e,o=t&&t.graphQLErrors&&t.graphQLErrors.map((e=>e.message));return{loading:r,types:n?n.reduce(((e,t)=>({...e,[t.key]:t})),{}):{},graphQLErrors:o}}},{READ:l}=r.graphqlTemplates;var a={apolloConfig:o,templateName:l,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,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 +/******/ (function() { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./client/src/boot/index.js": +/*!**********************************!*\ + !*** ./client/src/boot/index.js ***! + \**********************************/ +/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) { + + + +var _registerComponents = _interopRequireDefault(__webpack_require__(/*! ./registerComponents */ "./client/src/boot/registerComponents.js")); +var _registerQueries = _interopRequireDefault(__webpack_require__(/*! ./registerQueries */ "./client/src/boot/registerQueries.js")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +document.addEventListener('DOMContentLoaded', () => { + (0, _registerComponents.default)(); + (0, _registerQueries.default)(); +}); + +/***/ }), + +/***/ "./client/src/boot/registerComponents.js": +/*!***********************************************!*\ + !*** ./client/src/boot/registerComponents.js ***! + \***********************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _Injector = _interopRequireDefault(__webpack_require__(/*! lib/Injector */ "lib/Injector")); +var _LinkPicker = _interopRequireDefault(__webpack_require__(/*! components/LinkPicker/LinkPicker */ "./client/src/components/LinkPicker/LinkPicker.js")); +var _LinkField = _interopRequireDefault(__webpack_require__(/*! components/LinkField/LinkField */ "./client/src/components/LinkField/LinkField.js")); +var _LinkModal = _interopRequireDefault(__webpack_require__(/*! components/LinkModal/LinkModal */ "./client/src/components/LinkModal/LinkModal.js")); +var _FileLinkModal = _interopRequireDefault(__webpack_require__(/*! components/LinkModal/FileLinkModal */ "./client/src/components/LinkModal/FileLinkModal.js")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const registerComponents = () => { + _Injector.default.component.registerMany({ + LinkPicker: _LinkPicker.default, + LinkField: _LinkField.default, + 'LinkModal.FormBuilderModal': _LinkModal.default, + 'LinkModal.InsertMediaModal': _FileLinkModal.default + }); +}; +var _default = registerComponents; +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/boot/registerQueries.js": +/*!********************************************!*\ + !*** ./client/src/boot/registerQueries.js ***! + \********************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _Injector = _interopRequireDefault(__webpack_require__(/*! lib/Injector */ "lib/Injector")); +var _readLinkTypes = _interopRequireDefault(__webpack_require__(/*! state/linkTypes/readLinkTypes */ "./client/src/state/linkTypes/readLinkTypes.js")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const registerQueries = () => { + _Injector.default.query.register('readLinkTypes', _readLinkTypes.default); +}; +var _default = registerQueries; +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/components/LinkField/LinkField.js": +/*!******************************************************!*\ + !*** ./client/src/components/LinkField/LinkField.js ***! + \******************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _redux = __webpack_require__(/*! redux */ "redux"); +var _reactRedux = __webpack_require__(/*! react-redux */ "react-redux"); +var _Injector = __webpack_require__(/*! lib/Injector */ "lib/Injector"); +var _FieldHolder = _interopRequireDefault(__webpack_require__(/*! components/FieldHolder/FieldHolder */ "components/FieldHolder/FieldHolder")); +var _LinkPicker = _interopRequireDefault(__webpack_require__(/*! components/LinkPicker/LinkPicker */ "./client/src/components/LinkPicker/LinkPicker.js")); +var _LinkPickerTitle = _interopRequireDefault(__webpack_require__(/*! components/LinkPicker/LinkPickerTitle */ "./client/src/components/LinkPicker/LinkPickerTitle.js")); +var _LinkType = _interopRequireDefault(__webpack_require__(/*! types/LinkType */ "./client/src/types/LinkType.js")); +var _LinkModalContainer = _interopRequireDefault(__webpack_require__(/*! containers/LinkModalContainer */ "./client/src/containers/LinkModalContainer.js")); +var toastsActions = _interopRequireWildcard(__webpack_require__(/*! state/toasts/ToastsActions */ "state/toasts/ToastsActions")); +var _Backend = _interopRequireDefault(__webpack_require__(/*! lib/Backend */ "lib/Backend")); +var _Config = _interopRequireDefault(__webpack_require__(/*! lib/Config */ "lib/Config")); +var _propTypes = _interopRequireDefault(__webpack_require__(/*! prop-types */ "prop-types")); +var _i18n = _interopRequireDefault(__webpack_require__(/*! i18n */ "i18n")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController'; +const LinkField = _ref => { + var _data$editingID; + let { + value = null, + onChange, + types, + actions, + isMulti = false + } = _ref; + const [data, setData] = (0, _react.useState)({}); + const [editingID, setEditingID] = (0, _react.useState)(0); + let linkIDs = value; + if (!Array.isArray(linkIDs)) { + if (typeof linkIDs === 'number' && linkIDs != 0) { + linkIDs = [linkIDs]; + } + if (!linkIDs) { + linkIDs = []; + } + } + (0, _react.useEffect)(() => { + if (!editingID && linkIDs.length > 0) { + const query = []; + for (const linkID of linkIDs) { + query.push(`itemIDs[]=${linkID}`); + } + const endpoint = `${_Config.default.getSection(section).form.linkForm.dataUrl}?${query.join('&')}`; + _Backend.default.get(endpoint).then(response => response.json()).then(responseJson => { + setData(responseJson); + }); + } + }, [editingID, value && value.length]); + const onModalClosed = () => { + setEditingID(0); + }; + const onModalSuccess = value => { + setEditingID(0); + const ids = [...linkIDs]; + if (!ids.includes(value)) { + ids.push(value); + } + onChange(isMulti ? ids : ids[0]); + actions.toasts.success(_i18n.default._t('LinkField.SAVE_SUCCESS', 'Saved link')); + }; + const onClear = linkID => { + const endpoint = `${_Config.default.getSection(section).form.linkForm.deleteUrl}/${linkID}`; + _Backend.default.delete(endpoint, {}, { + 'X-SecurityID': _Config.default.get('SecurityID') + }).then(() => { + actions.toasts.success(_i18n.default._t('LinkField.DELETE_SUCCESS', 'Deleted link')); + }).catch(() => { + actions.toasts.error(_i18n.default._t('LinkField.DELETE_ERROR', 'Failed to delete link')); + }); + const newData = { + ...data + }; + delete newData[linkID]; + setData(newData); + onChange(isMulti ? Object.keys(newData) : 0); + }; + const renderLinks = () => { + const links = []; + for (const linkID of linkIDs) { + var _data$linkID, _data$linkID2, _data$linkID3, _data$linkID4, _data$linkID5, _data$linkID6; + const linkData = data[linkID]; + if (!linkData) { + continue; + } + console.log((_data$linkID = data[linkID]) === null || _data$linkID === void 0 ? void 0 : _data$linkID.versionState); + const type = types.hasOwnProperty((_data$linkID2 = data[linkID]) === null || _data$linkID2 === void 0 ? void 0 : _data$linkID2.typeKey) ? types[(_data$linkID3 = data[linkID]) === null || _data$linkID3 === void 0 ? void 0 : _data$linkID3.typeKey] : {}; + links.push(_react.default.createElement(_LinkPickerTitle.default, { + key: linkID, + id: linkID, + title: (_data$linkID4 = data[linkID]) === null || _data$linkID4 === void 0 ? void 0 : _data$linkID4.Title, + description: (_data$linkID5 = data[linkID]) === null || _data$linkID5 === void 0 ? void 0 : _data$linkID5.description, + versionState: (_data$linkID6 = data[linkID]) === null || _data$linkID6 === void 0 ? void 0 : _data$linkID6.versionState, + typeTitle: type.title || '', + onClear: onClear, + onClick: () => { + setEditingID(linkID); + } + })); + } + return links; + }; + const renderPicker = isMulti || Object.keys(data).length === 0; + const renderModal = Boolean(editingID); + return _react.default.createElement(_react.default.Fragment, null, renderPicker && _react.default.createElement(_LinkPicker.default, { + onModalSuccess: onModalSuccess, + onModalClosed: onModalClosed, + types: types + }), _react.default.createElement("div", null, " ", renderLinks(), " "), renderModal && _react.default.createElement(_LinkModalContainer.default, { + types: types, + typeKey: (_data$editingID = data[editingID]) === null || _data$editingID === void 0 ? void 0 : _data$editingID.typeKey, + isOpen: Boolean(editingID), + onSuccess: onModalSuccess, + onClosed: onModalClosed, + linkID: editingID + })); +}; +LinkField.propTypes = { + value: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.number), _propTypes.default.number]), + onChange: _propTypes.default.func.isRequired, + types: _propTypes.default.objectOf(_LinkType.default).isRequired, + actions: _propTypes.default.object.isRequired, + isMulti: _propTypes.default.bool +}; +const mapDispatchToProps = dispatch => ({ + actions: { + toasts: (0, _redux.bindActionCreators)(toastsActions, dispatch) + } +}); +var _default = (0, _redux.compose)((0, _Injector.injectGraphql)('readLinkTypes'), _FieldHolder.default, (0, _reactRedux.connect)(null, mapDispatchToProps))(LinkField); +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/components/LinkModal/FileLinkModal.js": +/*!**********************************************************!*\ + !*** ./client/src/components/LinkModal/FileLinkModal.js ***! + \**********************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _i18n = _interopRequireDefault(__webpack_require__(/*! i18n */ "i18n")); +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _InsertMediaModal = _interopRequireDefault(__webpack_require__(/*! containers/InsertMediaModal/InsertMediaModal */ "containers/InsertMediaModal/InsertMediaModal")); +var _reactRedux = __webpack_require__(/*! react-redux */ "react-redux"); +var _LinkType = _interopRequireDefault(__webpack_require__(/*! types/LinkType */ "./client/src/types/LinkType.js")); +var _propTypes = _interopRequireDefault(__webpack_require__(/*! prop-types */ "prop-types")); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } +const FileLinkModal = _ref => { + let { + type, + editing, + data, + actions, + onSubmit, + ...props + } = _ref; + if (!type) { + return false; + } + (0, _react.useEffect)(() => { + if (editing) { + actions.initModal(); + } else { + actions.reset(); + } + }, [editing]); + const attrs = data ? { + ID: data.FileID, + Description: data.Title, + TargetBlank: data.OpenInNew ? true : false + } : {}; + const onInsert = _ref2 => { + let { + ID, + Description, + TargetBlank + } = _ref2; + return onSubmit({ + FileID: ID, + Title: Description, + OpenInNew: TargetBlank, + typeKey: type.key + }, '', () => {}); + }; + return _react.default.createElement(_InsertMediaModal.default, _extends({ + isOpen: editing, + type: "insert-link", + title: false, + bodyClassName: "modal__dialog", + className: "insert-link__dialog-wrapper--internal", + fileAttributes: attrs, + onInsert: onInsert + }, props)); +}; +FileLinkModal.propTypes = { + type: _LinkType.default.isRequired, + editing: _propTypes.default.bool.isRequired, + data: _propTypes.default.object.isRequired, + actions: _propTypes.default.object.isRequired, + onClick: _propTypes.default.func.isRequired +}; +function mapStateToProps() { + return {}; +} +function mapDispatchToProps(dispatch) { + return { + actions: { + initModal: () => dispatch({ + type: 'INIT_FORM_SCHEMA_STACK', + payload: { + formSchema: { + type: 'insert-link', + nextType: 'admin' + } + } + }), + reset: () => dispatch({ + type: 'RESET' + }) + } + }; +} +var _default = (0, _reactRedux.connect)(mapStateToProps, mapDispatchToProps)(FileLinkModal); +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/components/LinkModal/LinkModal.js": +/*!******************************************************!*\ + !*** ./client/src/components/LinkModal/LinkModal.js ***! + \******************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _FormBuilderModal = _interopRequireDefault(__webpack_require__(/*! components/FormBuilderModal/FormBuilderModal */ "components/FormBuilderModal/FormBuilderModal")); +var _url = _interopRequireDefault(__webpack_require__(/*! url */ "url")); +var _qs = _interopRequireDefault(__webpack_require__(/*! qs */ "qs")); +var _Config = _interopRequireDefault(__webpack_require__(/*! lib/Config */ "lib/Config")); +var _propTypes = _interopRequireDefault(__webpack_require__(/*! prop-types */ "prop-types")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const buildSchemaUrl = (typeKey, linkID) => { + const { + schemaUrl + } = _Config.default.getSection('SilverStripe\\LinkField\\Controllers\\LinkFieldController').form.linkForm; + const parsedURL = _url.default.parse(schemaUrl); + const parsedQs = _qs.default.parse(parsedURL.query); + parsedQs.typeKey = typeKey; + for (const prop of ['href', 'path', 'pathname']) { + parsedURL[prop] = `${parsedURL[prop]}/${linkID}`; + } + return _url.default.format({ + ...parsedURL, + search: _qs.default.stringify(parsedQs) + }); +}; +const LinkModal = _ref => { + let { + typeTitle, + typeKey, + linkID = 0, + isOpen, + onSuccess, + onClosed + } = _ref; + if (!typeKey) { + return false; + } + const onSubmit = async (modalData, action, submitFn) => { + const formSchema = await submitFn(); + const hasValidationErrors = formSchema.id.match(/\/schema\/linkfield\/([0-9]+)/); + if (!hasValidationErrors) { + const match = formSchema.id.match(/\/linkForm\/([0-9]+)/); + const valueFromSchemaResponse = parseInt(match[1], 10); + onSuccess(valueFromSchemaResponse); + } + return Promise.resolve(); + }; + return _react.default.createElement(_FormBuilderModal.default, { + title: typeTitle, + isOpen: isOpen, + schemaUrl: buildSchemaUrl(typeKey, linkID), + identifier: "Link.EditingLinkInfo", + onSubmit: onSubmit, + onClosed: onClosed + }); +}; +LinkModal.propTypes = { + typeTitle: _propTypes.default.string.isRequired, + typeKey: _propTypes.default.string.isRequired, + linkID: _propTypes.default.number, + isOpen: _propTypes.default.bool.isRequired, + onSuccess: _propTypes.default.func.isRequired, + onClosed: _propTypes.default.func.isRequired +}; +LinkModal.defaultProps; +var _default = LinkModal; +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/components/LinkPicker/LinkPicker.js": +/*!********************************************************!*\ + !*** ./client/src/components/LinkPicker/LinkPicker.js ***! + \********************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = exports.Component = void 0; +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _propTypes = _interopRequireDefault(__webpack_require__(/*! prop-types */ "prop-types")); +var _classnames = _interopRequireDefault(__webpack_require__(/*! classnames */ "classnames")); +var _LinkPickerMenu = _interopRequireDefault(__webpack_require__(/*! ./LinkPickerMenu */ "./client/src/components/LinkPicker/LinkPickerMenu.js")); +var _LinkType = _interopRequireDefault(__webpack_require__(/*! types/LinkType */ "./client/src/types/LinkType.js")); +var _LinkModalContainer = _interopRequireDefault(__webpack_require__(/*! containers/LinkModalContainer */ "./client/src/containers/LinkModalContainer.js")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +const LinkPicker = _ref => { + let { + types, + onModalSuccess, + onModalClosed + } = _ref; + const [typeKey, setTypeKey] = (0, _react.useState)(''); + const handleSelect = key => { + setTypeKey(key); + }; + const handleClosed = () => { + if (typeof onModalClosed === 'function') { + onModalClosed(); + } + setTypeKey(''); + }; + const handleSuccess = value => { + setTypeKey(''); + onModalSuccess(value); + }; + const shouldOpenModal = typeKey !== ''; + const className = (0, _classnames.default)('link-picker', 'form-control'); + const typeArray = Object.values(types); + return _react.default.createElement("div", { + className: className + }, _react.default.createElement(_LinkPickerMenu.default, { + types: typeArray, + onSelect: handleSelect + }), shouldOpenModal && _react.default.createElement(_LinkModalContainer.default, { + types: types, + typeKey: typeKey, + isOpen: shouldOpenModal, + onSuccess: handleSuccess, + onClosed: handleClosed + })); +}; +exports.Component = LinkPicker; +LinkPicker.propTypes = { + types: _propTypes.default.objectOf(_LinkType.default).isRequired, + onModalSuccess: _propTypes.default.func.isRequired, + onModalClosed: _propTypes.default.func +}; +var _default = LinkPicker; +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/components/LinkPicker/LinkPickerMenu.js": +/*!************************************************************!*\ + !*** ./client/src/components/LinkPicker/LinkPickerMenu.js ***! + \************************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _i18n = _interopRequireDefault(__webpack_require__(/*! i18n */ "i18n")); +var _react = _interopRequireWildcard(__webpack_require__(/*! react */ "react")); +var _propTypes = _interopRequireDefault(__webpack_require__(/*! prop-types */ "prop-types")); +var _reactstrap = __webpack_require__(/*! reactstrap */ "reactstrap"); +var _LinkType = _interopRequireDefault(__webpack_require__(/*! types/LinkType */ "./client/src/types/LinkType.js")); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const LinkPickerMenu = _ref => { + let { + types, + onSelect + } = _ref; + const [isOpen, setIsOpen] = (0, _react.useState)(false); + const toggle = () => setIsOpen(prevState => !prevState); + return _react.default.createElement(_reactstrap.Dropdown, { + isOpen: isOpen, + toggle: toggle, + className: "link-picker__menu" + }, _react.default.createElement(_reactstrap.DropdownToggle, { + className: "link-picker__menu-toggle font-icon-plus-1", + caret: true + }, _i18n.default._t('LinkField.ADD_LINK', 'Add Link')), _react.default.createElement(_reactstrap.DropdownMenu, null, types.map(_ref2 => { + let { + key, + title + } = _ref2; + return _react.default.createElement(_reactstrap.DropdownItem, { + key: key, + onClick: () => onSelect(key) + }, title); + }))); +}; +LinkPickerMenu.propTypes = { + types: _propTypes.default.arrayOf(_LinkType.default).isRequired, + onSelect: _propTypes.default.func.isRequired +}; +var _default = LinkPickerMenu; +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/components/LinkPicker/LinkPickerTitle.js": +/*!*************************************************************!*\ + !*** ./client/src/components/LinkPicker/LinkPickerTitle.js ***! + \*************************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _classnames = _interopRequireDefault(__webpack_require__(/*! classnames */ "classnames")); +var _i18n = _interopRequireDefault(__webpack_require__(/*! i18n */ "i18n")); +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _propTypes = _interopRequireDefault(__webpack_require__(/*! prop-types */ "prop-types")); +var _reactstrap = __webpack_require__(/*! reactstrap */ "reactstrap"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const stopPropagation = fn => e => { + e.nativeEvent.stopImmediatePropagation(); + e.preventDefault(); + e.nativeEvent.preventDefault(); + e.stopPropagation(); + fn && fn(); +}; +const LinkPickerTitle = _ref => { + let { + id, + title, + description, + versionState, + typeTitle, + onClear, + onClick + } = _ref; + const classes = { + 'link-picker__link': true, + 'form-control': true + }; + if (versionState) { + classes[` link-picker__link--${versionState}`] = true; + } + const className = (0, _classnames.default)(classes); + return _react.default.createElement("div", { + className: className + }, _react.default.createElement(_reactstrap.Button, { + className: "link-picker__button font-icon-link", + color: "secondary", + onClick: stopPropagation(onClick) + }, _react.default.createElement("div", { + className: "link-picker__link-detail" + }, _react.default.createElement("div", { + className: "link-picker__title" + }, title), _react.default.createElement("small", { + className: "link-picker__type" + }, typeTitle, ":\xA0", _react.default.createElement("span", { + className: "link-picker__url" + }, description)))), _react.default.createElement(_reactstrap.Button, { + className: "link-picker__clear", + color: "link", + onClick: stopPropagation(() => onClear(id)) + }, _i18n.default._t('LinkField.CLEAR', 'Clear'))); +}; +LinkPickerTitle.propTypes = { + id: _propTypes.default.number.isRequired, + title: _propTypes.default.string, + description: _propTypes.default.string, + versionState: _propTypes.default.string, + typeTitle: _propTypes.default.string.isRequired, + onClear: _propTypes.default.func.isRequired, + onClick: _propTypes.default.func.isRequired +}; +var _default = LinkPickerTitle; +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/containers/LinkModalContainer.js": +/*!*****************************************************!*\ + !*** ./client/src/containers/LinkModalContainer.js ***! + \*****************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _Injector = __webpack_require__(/*! lib/Injector */ "lib/Injector"); +var _propTypes = _interopRequireDefault(__webpack_require__(/*! prop-types */ "prop-types")); +var _LinkType = _interopRequireDefault(__webpack_require__(/*! types/LinkType */ "./client/src/types/LinkType.js")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const LinkModalContainer = _ref => { + let { + types, + typeKey, + linkID = 0, + isOpen, + onSuccess, + onClosed + } = _ref; + if (!typeKey) { + return false; + } + const type = types.hasOwnProperty(typeKey) ? types[typeKey] : {}; + const handlerName = type && type.hasOwnProperty('handlerName') ? type.handlerName : 'FormBuilderModal'; + const LinkModal = (0, _Injector.loadComponent)(`LinkModal.${handlerName}`); + return _react.default.createElement(LinkModal, { + typeTitle: type.title || '', + typeKey: typeKey, + linkID: linkID, + isOpen: isOpen, + onSuccess: onSuccess, + onClosed: onClosed + }); +}; +LinkModalContainer.propTypes = { + types: _propTypes.default.objectOf(_LinkType.default).isRequired, + typeKey: _propTypes.default.string.isRequired, + linkID: _propTypes.default.number, + isOpen: _propTypes.default.bool.isRequired, + onSuccess: _propTypes.default.func.isRequired, + onClosed: _propTypes.default.func.isRequired +}; +var _default = LinkModalContainer; +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/entwine/LinkField.js": +/*!*****************************************!*\ + !*** ./client/src/entwine/LinkField.js ***! + \*****************************************/ +/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) { + + + +var _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ "jquery")); +var _react = _interopRequireDefault(__webpack_require__(/*! react */ "react")); +var _client = _interopRequireDefault(__webpack_require__(/*! react-dom/client */ "react-dom/client")); +var _Injector = __webpack_require__(/*! lib/Injector */ "lib/Injector"); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } +_jquery.default.entwine('ss', $ => { + $('.js-injector-boot .entwine-linkfield').entwine({ + Component: null, + Root: null, + onmatch() { + const cmsContent = this.closest('.cms-content').attr('id'); + const context = cmsContent ? { + context: cmsContent + } : {}; + const schemaComponent = this.data('schema-component'); + const ReactField = (0, _Injector.loadComponent)(schemaComponent, context); + this.setComponent(ReactField); + this.setRoot(_client.default.createRoot(this[0])); + this._super(); + this.refresh(); + }, + refresh() { + const props = this.getProps(); + this.getInputField().val(props.value); + const ReactField = this.getComponent(); + const Root = this.getRoot(); + Root.render(_react.default.createElement(ReactField, _extends({}, props, { + noHolder: true + }))); + }, + handleChange(value) { + this.getInputField().data('value', value); + this.refresh(); + }, + getProps() { + const value = this.getInputField().data('value'); + return { + value, + onChange: this.handleChange.bind(this), + isMulti: this.data('is-multi') ?? false + }; + }, + getInputField() { + const fieldID = this.data('field-id'); + return $(`#${fieldID}`); + }, + onunmatch() { + const Root = this.getRoot(); + if (Root) { + Root.unmount(); + } + } + }); +}); + +/***/ }), + +/***/ "./client/src/state/linkTypes/readLinkTypes.js": +/*!*****************************************************!*\ + !*** ./client/src/state/linkTypes/readLinkTypes.js ***! + \*****************************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _Injector = __webpack_require__(/*! lib/Injector */ "lib/Injector"); +const apolloConfig = { + props(props) { + const { + data: { + error, + readLinkTypes, + loading: networkLoading + } + } = props; + const errors = error && error.graphQLErrors && error.graphQLErrors.map(graphQLError => graphQLError.message); + const types = readLinkTypes ? readLinkTypes.reduce((accumulator, type) => ({ + ...accumulator, + [type.key]: type + }), {}) : {}; + return { + loading: networkLoading, + types, + graphQLErrors: errors + }; + } +}; +const { + READ +} = _Injector.graphqlTemplates; +const query = { + apolloConfig, + templateName: READ, + pluralName: 'LinkTypes', + pagination: false, + params: { + keys: '[ID]' + }, + args: { + root: { + keys: 'keys' + } + }, + fields: ['key', 'title'] +}; +var _default = query; +exports["default"] = _default; + +/***/ }), + +/***/ "./client/src/types/LinkType.js": +/*!**************************************!*\ + !*** ./client/src/types/LinkType.js ***! + \**************************************/ +/***/ (function(__unused_webpack_module, exports, __webpack_require__) { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _propTypes = _interopRequireDefault(__webpack_require__(/*! prop-types */ "prop-types")); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const LinkType = _propTypes.default.shape({ + key: _propTypes.default.string.isRequired, + title: _propTypes.default.string.isRequired +}); +var _default = LinkType; +exports["default"] = _default; + +/***/ }), + +/***/ "lib/Backend": +/*!**************************!*\ + !*** external "Backend" ***! + \**************************/ +/***/ (function(module) { + +module.exports = Backend; + +/***/ }), + +/***/ "lib/Config": +/*!*************************!*\ + !*** external "Config" ***! + \*************************/ +/***/ (function(module) { + +module.exports = Config; + +/***/ }), + +/***/ "components/FieldHolder/FieldHolder": +/*!******************************!*\ + !*** external "FieldHolder" ***! + \******************************/ +/***/ (function(module) { + +module.exports = FieldHolder; + +/***/ }), + +/***/ "components/FormBuilderModal/FormBuilderModal": +/*!***********************************!*\ + !*** external "FormBuilderModal" ***! + \***********************************/ +/***/ (function(module) { + +module.exports = FormBuilderModal; + +/***/ }), + +/***/ "lib/Injector": +/*!***************************!*\ + !*** external "Injector" ***! + \***************************/ +/***/ (function(module) { + +module.exports = Injector; + +/***/ }), + +/***/ "containers/InsertMediaModal/InsertMediaModal": +/*!***********************************!*\ + !*** external "InsertMediaModal" ***! + \***********************************/ +/***/ (function(module) { + +module.exports = InsertMediaModal; + +/***/ }), + +/***/ "url": +/*!**************************!*\ + !*** external "NodeUrl" ***! + \**************************/ +/***/ (function(module) { + +module.exports = NodeUrl; + +/***/ }), + +/***/ "prop-types": +/*!****************************!*\ + !*** external "PropTypes" ***! + \****************************/ +/***/ (function(module) { + +module.exports = PropTypes; + +/***/ }), + +/***/ "react": +/*!************************!*\ + !*** external "React" ***! + \************************/ +/***/ (function(module) { + +module.exports = React; + +/***/ }), + +/***/ "react-dom/client": +/*!*********************************!*\ + !*** external "ReactDomClient" ***! + \*********************************/ +/***/ (function(module) { + +module.exports = ReactDomClient; + +/***/ }), + +/***/ "react-redux": +/*!*****************************!*\ + !*** external "ReactRedux" ***! + \*****************************/ +/***/ (function(module) { + +module.exports = ReactRedux; + +/***/ }), + +/***/ "reactstrap": +/*!*****************************!*\ + !*** external "Reactstrap" ***! + \*****************************/ +/***/ (function(module) { + +module.exports = Reactstrap; + +/***/ }), + +/***/ "redux": +/*!************************!*\ + !*** external "Redux" ***! + \************************/ +/***/ (function(module) { + +module.exports = Redux; + +/***/ }), + +/***/ "state/toasts/ToastsActions": +/*!********************************!*\ + !*** external "ToastsActions" ***! + \********************************/ +/***/ (function(module) { + +module.exports = ToastsActions; + +/***/ }), + +/***/ "classnames": +/*!*****************************!*\ + !*** external "classnames" ***! + \*****************************/ +/***/ (function(module) { + +module.exports = classnames; + +/***/ }), + +/***/ "i18n": +/*!***********************!*\ + !*** external "i18n" ***! + \***********************/ +/***/ (function(module) { + +module.exports = i18n; + +/***/ }), + +/***/ "jquery": +/*!*************************!*\ + !*** external "jQuery" ***! + \*************************/ +/***/ (function(module) { + +module.exports = jQuery; + +/***/ }), + +/***/ "qs": +/*!*********************!*\ + !*** external "qs" ***! + \*********************/ +/***/ (function(module) { + +module.exports = qs; + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +!function() { +/*!**************************************!*\ + !*** ./client/src/bundles/bundle.js ***! + \**************************************/ + + +__webpack_require__(/*! boot */ "./client/src/boot/index.js"); +__webpack_require__(/*! entwine/LinkField */ "./client/src/entwine/LinkField.js"); +}(); +/******/ })() +; +//# sourceMappingURL=bundle.js.map \ No newline at end of file diff --git a/client/dist/styles/bundle.css b/client/dist/styles/bundle.css index b8ebcc27..523f83a6 100644 --- a/client/dist/styles/bundle.css +++ b/client/dist/styles/bundle.css @@ -1 +1,109 @@ -.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}.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__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} +/*!*****************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[0].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[0].use[2]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[0].use[3]!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[0].use[4]!./client/src/styles/bundle.scss ***! + \*****************************************************************************************************************************************************************************************************************************************************************************************************************************/ +.link-picker__link, .link-picker { + display: flex; + height: auto; + width: 100%; + min-height: 54px; + background: white; + 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: 0.76925rem; +} + +.link-picker__menu { + flex-grow: 1; +} + +.link-picker__menu-toggle { + width: 100%; + height: 100%; + text-align: left; +} +.link-picker__menu-toggle::before { + padding: 0.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 { + color: red; + position: absolute; + top: 24px; + left: 32px; + content: "default"; + z-index: 1; +} +.link-picker__link--unsaved::before { + content: "unsaved"; +} +.link-picker__link--draft::before { + content: "draft"; +} +.link-picker__link--modified::before { + content: "modified"; +} +.link-picker__link--published::before { + content: "published"; +} + +.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: 0.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; +} + +/*# sourceMappingURL=bundle.css.map*/ \ No newline at end of file diff --git a/client/src/components/LinkField/LinkField.js b/client/src/components/LinkField/LinkField.js index 4cc94e6a..8f94bcd6 100644 --- a/client/src/components/LinkField/LinkField.js +++ b/client/src/components/LinkField/LinkField.js @@ -135,12 +135,15 @@ const LinkField = ({ value = null, onChange, types, actions, isMulti = false }) continue; } + console.log(data[linkID]?.versionState); + const type = types.hasOwnProperty(data[linkID]?.typeKey) ? types[data[linkID]?.typeKey] : {}; links.push( { setEditingID(linkID); }} diff --git a/client/src/components/LinkPicker/LinkPicker.scss b/client/src/components/LinkPicker/LinkPicker.scss index ee68a8ea..c0278cea 100644 --- a/client/src/components/LinkPicker/LinkPicker.scss +++ b/client/src/components/LinkPicker/LinkPicker.scss @@ -45,6 +45,7 @@ text-align: left; margin-right: 0; justify-content: space-between; + position: relative; &:not(:last-child) { border-bottom: 0; @@ -63,6 +64,31 @@ text-decoration: none; color: inherit; } + + &::before { + color: red; + position: absolute; + top: 24px; + left: 32px; + content: 'default'; + z-index: 1; + } + + &--unsaved::before { + content: 'unsaved'; + } + + &--draft::before { + content: 'draft'; + } + + &--modified::before { + content: 'modified'; + } + + &--published::before { + content: 'published'; + } } .link-picker__button { diff --git a/client/src/components/LinkPicker/LinkPickerTitle.js b/client/src/components/LinkPicker/LinkPickerTitle.js index 9e069fca..41e7f6e2 100644 --- a/client/src/components/LinkPicker/LinkPickerTitle.js +++ b/client/src/components/LinkPicker/LinkPickerTitle.js @@ -1,4 +1,5 @@ /* eslint-disable */ +import classnames from 'classnames'; import i18n from 'i18n'; import React from 'react'; import PropTypes from 'prop-types'; @@ -12,8 +13,16 @@ const stopPropagation = (fn) => (e) => { fn && fn(); } -const LinkPickerTitle = ({ id, title, description, typeTitle, onClear, onClick }) => ( -
+const LinkPickerTitle = ({ id, title, description, versionState, typeTitle, onClear, onClick }) => { + const classes = { + 'link-picker__link': true, + 'form-control': true, + }; + if (versionState) { + classes[` link-picker__link--${versionState}`] = true; + } + const className = classnames(classes); + return
-); +}; LinkPickerTitle.propTypes = { id: PropTypes.number.isRequired, title: PropTypes.string, description: PropTypes.string, + versionState: PropTypes.string, typeTitle: PropTypes.string.isRequired, onClear: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired, diff --git a/composer.json b/composer.json index 7061a0eb..d743c3f3 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,8 @@ "type": "silverstripe-vendormodule", "require": { "php": "^8.1", - "silverstripe/cms": "^5" + "silverstripe/cms": "^5", + "silverstripe/versioned": "^2" }, "require-dev": { "silverstripe/recipe-testing": "^3", diff --git a/src/Controllers/LinkFieldController.php b/src/Controllers/LinkFieldController.php index 309d991f..860f082c 100644 --- a/src/Controllers/LinkFieldController.php +++ b/src/Controllers/LinkFieldController.php @@ -20,6 +20,7 @@ use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\HiddenField; use SilverStripe\ORM\DataList; +use SilverStripe\Versioned\Versioned; class LinkFieldController extends LeftAndMain { @@ -113,6 +114,7 @@ private function getLinkData(Link $link): array } $data = $link->jsonSerialize(); $data['description'] = $link->getDescription(); + $data['versionState'] = $this->getVersionedState($link); return $data; } @@ -343,6 +345,25 @@ private function itemIDsFromRequest(): array return $idsAsInt; } + private function getVersionedState(Link $link): string + { + if (!$link->exists()) { + return 'unsaved'; + } + if (Link::singleton()->hasExtension(Versioned::class)) { + if ($link->isModifiedOnDraft()) { + return 'modified'; + } + if ($link->isPublished()) { + return 'published'; + } + return 'draft'; + } + // Unversioned - links are saved in the modal so there is no 'dirty state' and + // when undversioned saved is the same thing as published + return 'published'; + } + /** * Get the ?typeKey request querystring param */ diff --git a/src/Models/Link.php b/src/Models/Link.php index 315e08b6..b7ba7d03 100644 --- a/src/Models/Link.php +++ b/src/Models/Link.php @@ -5,7 +5,6 @@ use InvalidArgumentException; use ReflectionException; use SilverStripe\Core\ClassInfo; -use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\CompositeValidator; use SilverStripe\Forms\DropdownField; @@ -14,6 +13,7 @@ use SilverStripe\LinkField\Type\Registry; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBHTMLText; +use SilverStripe\Versioned\Versioned; /** * A Link Data Object. This class should be a subclass, and you should never directly interact with a plain Link @@ -31,6 +31,15 @@ class Link extends DataObject 'OpenInNew' => 'Boolean', ]; + // don't merge this, this will get added in a different card + private static array $has_one = [ + 'Parent' => DataObject::class, + ]; + + private static array $extensions = [ + Versioned::class, + ]; + /** * In-memory only property used to change link type * This case is relevant for CMS edit form which doesn't use React driven UI diff --git a/tests/php/Models/LinkTest.php b/tests/php/Models/LinkTest.php index ad02ef3d..33c6053d 100644 --- a/tests/php/Models/LinkTest.php +++ b/tests/php/Models/LinkTest.php @@ -45,11 +45,6 @@ protected function setUp(): void $image = $this->objFromFixture(Image::class, 'image-1'); $image->setFromLocalFile(dirname(dirname(dirname(__FILE__))) . '/resources/600x400.png'); $image->write(); - $image->publishSingle(); - - /** @var SiteTree $page */ - $page = $this->objFromFixture(SiteTree::class, 'page-1'); - $page->publishSingle(); } protected function tearDown(): void @@ -250,14 +245,9 @@ public function linkTypeEnabledProvider(): array */ public function testGetUrl(string $identifier, string $class, string $expected): void { - Versioned::withVersionedMode(function () use ($identifier, $class, $expected): void { - Versioned::set_stage(Versioned::LIVE); - - /** @var Link $link */ - $link = $this->objFromFixture($class, $identifier); - - $this->assertSame($expected, $link->getURL(), 'We expect specific URL value'); - }); + /** @var Link $link */ + $link = $this->objFromFixture($class, $identifier); + $this->assertSame($expected, $link->getURL(), 'We expect specific URL value'); } public function linkUrlCasesDataProvider(): array @@ -326,7 +316,7 @@ public function linkUrlCasesDataProvider(): array 'file link / with image' => [ 'file-link-with-image', FileLink::class, - '/assets/ImageTest/600x400.png', + '/assets/8cf6c65fa7/600x400.png', ], 'file link / no image' => [ 'file-link-no-image',