From f00a449efcad05237bd543073f5274a2e181ec48 Mon Sep 17 00:00:00 2001 From: Sabina Talipova Date: Mon, 13 Nov 2023 15:47:31 +1300 Subject: [PATCH] ENH Add localization support --- client/dist/js/bundle.js | 2 +- client/lang/en.js | 76 ++---------------- client/lang/src/en.json | 78 ++----------------- client/src/boot/registerComponents.js | 1 - client/src/components/LinkField/LinkField.js | 23 +++++- .../components/LinkPicker/LinkPickerMenu.js | 2 +- .../components/LinkPicker/LinkPickerTitle.js | 2 +- lang/en.yml | 34 ++++++++ src/Controllers/LinkFieldController.php | 36 +++++---- src/GraphQL/LinkTypeResolver.php | 2 +- src/Models/EmailLink.php | 15 ++-- src/Models/ExternalLink.php | 11 +++ src/Models/FileLink.php | 10 +++ src/Models/Link.php | 62 ++++++++++++--- src/Models/PhoneLink.php | 11 +++ src/Models/SiteTreeLink.php | 31 ++++++-- src/Tasks/LinkableMigrationTask.php | 31 ++++++-- src/Type/Registry.php | 21 ++++- 18 files changed, 252 insertions(+), 196 deletions(-) create mode 100644 lang/en.yml diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js index 4ca2af57..7981fd31 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)),i=o(n(154));function o(e){return e&&e.__esModule?e:{default:e}}document.addEventListener("DOMContentLoaded",(()=>{(0,r.default)(),(0,i.default)()}))},521:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(648)),i=u(n(809)),o=u(n(852)),a=u(n(117)),l=u(n(606));function u(e){return e&&e.__esModule?e:{default:e}}var d=()=>{r.default.component.registerMany({LinkPicker:i.default,LinkField:o.default,"LinkModal.FormBuilderModal":a.default,"LinkModal.InsertMediaModal":l.default})};t.default=d},154:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=o(n(648)),i=o(n(689));function o(e){return e&&e.__esModule?e:{default:e}}var a=()=>{r.default.query.register("readLinkTypes",i.default)};t.default=a},852:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=m(n(363)),i=n(827),o=n(624),a=n(648),l=p(n(42)),u=p(n(809)),d=m(n(123)),s=p(n(159)),f=p(n(510)),c=p(n(86));function p(e){return e&&e.__esModule?e:{default:e}}function y(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(y=function(e){return e?n:t})(e)}function m(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=y(t);if(n&&n.has(e))return n.get(e);var r={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var a=i?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 k="SilverStripe\\LinkField\\Controllers\\LinkFieldController",v=e=>{let{value:t,onChange:n,types:i,actions:o}=e;const l=t,[d,c]=(0,r.useState)(""),[p,y]=(0,r.useState)({}),[m,v]=(0,r.useState)(!1),_=p.Title||"",g=i.hasOwnProperty(d)?i[d]:{},h=d?i[d]:g,b=h&&h.hasOwnProperty("handlerName")?h.handlerName:"FormBuilderModal",O=(0,a.loadComponent)(`LinkModal.${b}`),M={title:_,description:p.description,typeTitle:g.title||"",onEdit:()=>{v(!0)},onClear:()=>{const e=`${f.default.getSection(k).form.linkForm.deleteUrl}/${l}`;s.default.delete(e,{},{"X-SecurityID":f.default.get("SecurityID")}).then((()=>{o.toasts.success("Deleted link")})).catch((()=>{o.toasts.error("Failed to delete link")})),c(""),y({}),n(0)},onSelect:e=>{c(e),v(!0)},types:Object.values(i)},j={typeTitle:g.title||"",typeKey:d,editing:m,onSubmit:async(e,t,r)=>{const i=await r();if(!i.id.match(/\/schema\/linkfield\/([0-9]+)/)){const e=i.id.match(/\/linkForm\/([0-9]+)/),t=parseInt(e[1],10);v(!1),n(t),o.toasts.success("Saved link")}return Promise.resolve()},onClosed:()=>{v(!1)},linkID:l};return(0,r.useEffect)((()=>{if(!m&&l){const e=`${f.default.getSection(k).form.linkForm.dataUrl}/${l}`;s.default.get(e).then((e=>e.json())).then((e=>{y(e),c(e.typeKey)}))}}),[m,l]),r.default.createElement(r.default.Fragment,null,r.default.createElement(u.default,M),r.default.createElement(O,j))};v.propTypes={value:c.default.number.isRequired,onChange:c.default.func.isRequired};var _=(0,i.compose)((0,a.injectGraphql)("readLinkTypes"),l.default,(0,o.connect)(null,(e=>({actions:{toasts:(0,i.bindActionCreators)(d,e)}}))))(v);t.default=_},606:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;d(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={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var a=i?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)),i=d(n(475)),o=n(624),a=d(n(686)),l=d(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 d(e){return e&&e.__esModule?e:{default:e}}function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let{type:t,editing:n,data:o,actions:a,onSubmit:l,...u}=e;if(!t)return!1;(0,r.useEffect)((()=>{n?a.initModal():a.reset()}),[n]);const d=o?{ID:o.FileID,Description:o.Title,TargetBlank:!!o.OpenInNew}:{};return r.default.createElement(i.default,s({isOpen:n,type:"insert-link",title:!1,bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:d,onInsert:e=>{let{ID:n,Description:r,TargetBlank:i}=e;return l({FileID:n,Title:r,OpenInNew:i,typeKey:t.key},"",(()=>{}))}},u))};f.propTypes={type:a.default.isRequired,editing:l.default.bool.isRequired,data:l.default.object.isRequired,actions:l.default.object.isRequired,onClick:l.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=d(n(363)),i=d(n(912)),o=d(n(872)),a=d(n(902)),l=d(n(510)),u=d(n(86));function d(e){return e&&e.__esModule?e:{default:e}}const s=(e,t)=>{const{schemaUrl:n}=l.default.getSection("SilverStripe\\LinkField\\Controllers\\LinkFieldController").form.linkForm,r=o.default.parse(n),i=a.default.parse(r.query);i.typeKey=e;for(const e of["href","path","pathname"])r[e]=`${r[e]}/${t}`;return o.default.format({...r,search:a.default.stringify(i)})},f=e=>{let{typeTitle:t,typeKey:n,linkID:o,editing:a,onSubmit:l,onClosed:u}=e;return!!n&&r.default.createElement(i.default,{title:t,isOpen:a,schemaUrl:s(n,o),identifier:"Link.EditingLinkInfo",onSubmit:l,onClosed:u})};f.propTypes={typeTitle:u.default.string.isRequired,typeKey:u.default.string.isRequired,linkID:u.default.number.isRequired,editing:u.default.bool.isRequired,onSubmit: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=u(n(363)),i=u(n(86)),o=u(n(820)),a=u(n(97)),l=u(n(734));function u(e){return e&&e.__esModule?e:{default:e}}const d=e=>{let{title:t,description:n,typeTitle:i,types:u,onSelect:d,onEdit:s,onClear:f}=e;return r.default.createElement("div",{className:(0,o.default)("link-picker","form-control",{"link-picker--selected":!!i})},!i&&r.default.createElement(a.default,{types:u,onSelect:d}),i&&r.default.createElement(l.default,{title:t,description:n,typeTitle:i,onClear:f,onClick:()=>s()}))};t.Component=d,d.propTypes={...a.default.propTypes,title:i.default.string,description:i.default.string,typeTitle:i.default.string.isRequired,onEdit:i.default.func.isRequired,onClear:i.default.func.isRequired,onSelect:i.default.func.isRequired};var s=d;t.default=s},97:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=d(n(754)),i=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={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var a=i?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=d(n(86)),a=n(127),l=d(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 d(e){return e&&e.__esModule?e:{default:e}}const s=e=>{let{types:t,onSelect:n}=e;const[o,l]=(0,i.useState)(!1);return i.default.createElement(a.Dropdown,{isOpen:o,toggle:()=>l((e=>!e)),className:"link-picker__menu"},i.default.createElement(a.DropdownToggle,{className:"link-picker__menu-toggle font-icon-link",caret:!0},r.default._t("Link.ADD_LINK","Add Link")),i.default.createElement(a.DropdownMenu,null,t.map((e=>{let{key:t,title:r}=e;return i.default.createElement(a.DropdownItem,{key:t,onClick:()=>n(t)},r)}))))};s.propTypes={types:o.default.arrayOf(l.default).isRequired,onSelect:o.default.func.isRequired};var f=s;t.default=f},734:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=l(n(754)),i=l(n(363)),o=l(n(86)),a=n(127);function l(e){return e&&e.__esModule?e:{default:e}}const u=e=>t=>{t.nativeEvent.stopImmediatePropagation(),t.preventDefault(),t.nativeEvent.preventDefault(),t.stopPropagation(),e&&e()},d=e=>{let{title:t,description:n,typeTitle:o,onClear:l,onClick:d}=e;return i.default.createElement("div",{className:"link-picker__link"},i.default.createElement(a.Button,{className:"link-picker__button font-icon-link",color:"secondary",onClick:u(d)},i.default.createElement("div",{className:"link-picker__link-detail"},i.default.createElement("div",{className:"link-picker__title"},t),i.default.createElement("small",{className:"link-picker__type"},o,": ",i.default.createElement("span",{className:"link-picker__url"},n)))),i.default.createElement(a.Button,{className:"link-picker__clear",color:"link",onClick:u(l)},r.default._t("Link.CLEAR","Clear")))};d.propTypes={title:o.default.string,description:o.default.string,typeTitle:o.default.string.isRequired,onClear:o.default.func.isRequired,onClick:o.default.func.isRequired};var s=d;t.default=s},41:function(e,t,n){var r=l(n(311)),i=l(n(363)),o=l(n(691)),a=n(648);function l(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(),t=this.getComponent();this.getRoot().render(i.default.createElement(t,u({},e,{noHolder:!0})))},handleChange(t){const n=e(this).data("field-id");e("#"+n).val(t),this.refresh()},getProps(){const t=e(this).data("field-id");return{value:Number(e("#"+t).val()),onChange:this.handleChange.bind(this)}},onunmatch(){this.getRoot().unmount()}})}))},689:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n(648);const i={props(e){const{data:{error:t,readLinkTypes:n,loading:r}}=e,i=t&&t.graphQLErrors&&t.graphQLErrors.map((e=>e.message));return{loading:r,types:n?n.reduce(((e,t)=>({...e,[t.key]:t})),{}):{},graphQLErrors:i}}},{READ:o}=r.graphqlTemplates;var a={apolloConfig:i,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,i=(r=n(86))&&r.__esModule?r:{default:r};var o=i.default.shape({key:i.default.string.isRequired,title:i.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 i=t[r];if(void 0!==i)return i.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,n),o.exports}n(274),n(41)}(); \ No newline at end of file +!function(){"use strict";var e={274:function(e,t,n){var r=o(n(521)),i=o(n(154));function o(e){return e&&e.__esModule?e:{default:e}}document.addEventListener("DOMContentLoaded",(()=>{(0,r.default)(),(0,i.default)()}))},521:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=u(n(648)),i=u(n(809)),o=u(n(852)),l=u(n(117)),a=u(n(606));function u(e){return e&&e.__esModule?e:{default:e}}var d=()=>{r.default.component.registerMany({LinkPicker:i.default,LinkField:o.default,"LinkModal.FormBuilderModal":l.default,"LinkModal.InsertMediaModal":a.default})};t.default=d},154:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=o(n(648)),i=o(n(689));function o(e){return e&&e.__esModule?e:{default:e}}var l=()=>{r.default.query.register("readLinkTypes",i.default)};t.default=l},852:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=k(n(363)),i=n(827),o=n(624),l=n(648),a=y(n(42)),u=y(n(809)),d=k(n(123)),s=y(n(159)),f=y(n(510)),c=y(n(86)),p=y(n(754));function y(e){return e&&e.__esModule?e:{default:e}}function m(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(m=function(e){return e?n:t})(e)}function k(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=m(t);if(n&&n.has(e))return n.get(e);var r={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var l=i?Object.getOwnPropertyDescriptor(e,o):null;l&&(l.get||l.set)?Object.defineProperty(r,o,l):r[o]=e[o]}return r.default=e,n&&n.set(e,r),r}const v="SilverStripe\\LinkField\\Controllers\\LinkFieldController",_=e=>{let{value:t,onChange:n,types:i,actions:o}=e;const a=t,[d,c]=(0,r.useState)(""),[y,m]=(0,r.useState)({}),[k,_]=(0,r.useState)(!1),g=y.Title||"",h=i.hasOwnProperty(d)?i[d]:{},b=d?i[d]:h,O=b&&b.hasOwnProperty("handlerName")?b.handlerName:"FormBuilderModal",M=(0,l.loadComponent)(`LinkModal.${O}`),C={title:g,description:y.description,typeTitle:h.title||"",onEdit:()=>{_(!0)},onClear:()=>{const e=`${f.default.getSection(v).form.linkForm.deleteUrl}/${a}`;s.default.delete(e,{},{"X-SecurityID":f.default.get("SecurityID")}).then((()=>{o.toasts.success(p.default._t("LinkField.DELETE_SUCCESS","Deleted link"))})).catch((()=>{o.toasts.error(p.default._t("LinkField.DELETE_ERROR","Failed to delete link"))})),c(""),m({}),n(0)},onSelect:e=>{c(e),_(!0)},types:Object.values(i)},E={typeTitle:h.title||"",typeKey:d,editing:k,onSubmit:async(e,t,r)=>{const i=await r();if(!i.id.match(/\/schema\/linkfield\/([0-9]+)/)){const e=i.id.match(/\/linkForm\/([0-9]+)/),t=parseInt(e[1],10);_(!1),n(t),o.toasts.success(p.default._t("LinkField.SAVE_SUCCESS","Saved link"))}return Promise.resolve()},onClosed:()=>{_(!1)},linkID:a};return(0,r.useEffect)((()=>{if(!k&&a){const e=`${f.default.getSection(v).form.linkForm.dataUrl}/${a}`;s.default.get(e).then((e=>e.json())).then((e=>{m(e),c(e.typeKey)}))}}),[k,a]),r.default.createElement(r.default.Fragment,null,r.default.createElement(u.default,C),r.default.createElement(M,E))};_.propTypes={value:c.default.number.isRequired,onChange:c.default.func.isRequired};var g=(0,i.compose)((0,l.injectGraphql)("readLinkTypes"),a.default,(0,o.connect)(null,(e=>({actions:{toasts:(0,i.bindActionCreators)(d,e)}}))))(_);t.default=g},606:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;d(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={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var l=i?Object.getOwnPropertyDescriptor(e,o):null;l&&(l.get||l.set)?Object.defineProperty(r,o,l):r[o]=e[o]}r.default=e,n&&n.set(e,r);return r}(n(363)),i=d(n(475)),o=n(624),l=d(n(686)),a=d(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 d(e){return e&&e.__esModule?e:{default:e}}function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t{let{type:t,editing:n,data:o,actions:l,onSubmit:a,...u}=e;if(!t)return!1;(0,r.useEffect)((()=>{n?l.initModal():l.reset()}),[n]);const d=o?{ID:o.FileID,Description:o.Title,TargetBlank:!!o.OpenInNew}:{};return r.default.createElement(i.default,s({isOpen:n,type:"insert-link",title:!1,bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:d,onInsert:e=>{let{ID:n,Description:r,TargetBlank:i}=e;return a({FileID:n,Title:r,OpenInNew:i,typeKey:t.key},"",(()=>{}))}},u))};f.propTypes={type:l.default.isRequired,editing:a.default.bool.isRequired,data:a.default.object.isRequired,actions:a.default.object.isRequired,onClick:a.default.func.isRequired};var c=(0,o.connect)((function(){return{}}),(function(e){return{actions:{initModal:()=>e({type:"INIT_FORM_SCHEMA_STACK",payload:{formSchema:{type:"insert-link",nextType:"admin"}}}),reset:()=>e({type:"RESET"})}}}))(f);t.default=c},117:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=d(n(363)),i=d(n(912)),o=d(n(872)),l=d(n(902)),a=d(n(510)),u=d(n(86));function d(e){return e&&e.__esModule?e:{default:e}}const s=(e,t)=>{const{schemaUrl:n}=a.default.getSection("SilverStripe\\LinkField\\Controllers\\LinkFieldController").form.linkForm,r=o.default.parse(n),i=l.default.parse(r.query);i.typeKey=e;for(const e of["href","path","pathname"])r[e]=`${r[e]}/${t}`;return o.default.format({...r,search:l.default.stringify(i)})},f=e=>{let{typeTitle:t,typeKey:n,linkID:o,editing:l,onSubmit:a,onClosed:u}=e;return!!n&&r.default.createElement(i.default,{title:t,isOpen:l,schemaUrl:s(n,o),identifier:"Link.EditingLinkInfo",onSubmit:a,onClosed:u})};f.propTypes={typeTitle:u.default.string.isRequired,typeKey:u.default.string.isRequired,linkID:u.default.number.isRequired,editing:u.default.bool.isRequired,onSubmit: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=u(n(363)),i=u(n(86)),o=u(n(820)),l=u(n(97)),a=u(n(734));function u(e){return e&&e.__esModule?e:{default:e}}const d=e=>{let{title:t,description:n,typeTitle:i,types:u,onSelect:d,onEdit:s,onClear:f}=e;return r.default.createElement("div",{className:(0,o.default)("link-picker","form-control",{"link-picker--selected":!!i})},!i&&r.default.createElement(l.default,{types:u,onSelect:d}),i&&r.default.createElement(a.default,{title:t,description:n,typeTitle:i,onClear:f,onClick:()=>s()}))};t.Component=d,d.propTypes={...l.default.propTypes,title:i.default.string,description:i.default.string,typeTitle:i.default.string.isRequired,onEdit:i.default.func.isRequired,onClear:i.default.func.isRequired,onSelect:i.default.func.isRequired};var s=d;t.default=s},97:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=d(n(754)),i=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={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if("default"!==o&&Object.prototype.hasOwnProperty.call(e,o)){var l=i?Object.getOwnPropertyDescriptor(e,o):null;l&&(l.get||l.set)?Object.defineProperty(r,o,l):r[o]=e[o]}r.default=e,n&&n.set(e,r);return r}(n(363)),o=d(n(86)),l=n(127),a=d(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 d(e){return e&&e.__esModule?e:{default:e}}const s=e=>{let{types:t,onSelect:n}=e;const[o,a]=(0,i.useState)(!1);return i.default.createElement(l.Dropdown,{isOpen:o,toggle:()=>a((e=>!e)),className:"link-picker__menu"},i.default.createElement(l.DropdownToggle,{className:"link-picker__menu-toggle font-icon-link",caret:!0},r.default._t("LinkField.ADD_LINK","Add Link")),i.default.createElement(l.DropdownMenu,null,t.map((e=>{let{key:t,title:r}=e;return i.default.createElement(l.DropdownItem,{key:t,onClick:()=>n(t)},r)}))))};s.propTypes={types:o.default.arrayOf(a.default).isRequired,onSelect:o.default.func.isRequired};var f=s;t.default=f},734:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=a(n(754)),i=a(n(363)),o=a(n(86)),l=n(127);function a(e){return e&&e.__esModule?e:{default:e}}const u=e=>t=>{t.nativeEvent.stopImmediatePropagation(),t.preventDefault(),t.nativeEvent.preventDefault(),t.stopPropagation(),e&&e()},d=e=>{let{title:t,description:n,typeTitle:o,onClear:a,onClick:d}=e;return i.default.createElement("div",{className:"link-picker__link"},i.default.createElement(l.Button,{className:"link-picker__button font-icon-link",color:"secondary",onClick:u(d)},i.default.createElement("div",{className:"link-picker__link-detail"},i.default.createElement("div",{className:"link-picker__title"},t),i.default.createElement("small",{className:"link-picker__type"},o,": ",i.default.createElement("span",{className:"link-picker__url"},n)))),i.default.createElement(l.Button,{className:"link-picker__clear",color:"link",onClick:u(a)},r.default._t("LinkField.CLEAR","Clear")))};d.propTypes={title:o.default.string,description:o.default.string,typeTitle:o.default.string.isRequired,onClear:o.default.func.isRequired,onClick:o.default.func.isRequired};var s=d;t.default=s},41:function(e,t,n){var r=a(n(311)),i=a(n(363)),o=a(n(691)),l=n(648);function a(e){return e&&e.__esModule?e:{default:e}}function u(){return u=Object.assign?Object.assign.bind():function(e){for(var t=1;t{e(".js-injector-boot .entwine-linkfield").entwine({Component:null,Root:null,onmatch(){const e=this.closest(".cms-content").attr("id"),t=e?{context:e}:{},n=this.data("schema-component"),r=(0,l.loadComponent)(n,t);this.setComponent(r),this.setRoot(o.default.createRoot(this[0])),this._super(),this.refresh()},refresh(){const e=this.getProps(),t=this.getComponent();this.getRoot().render(i.default.createElement(t,u({},e,{noHolder:!0})))},handleChange(t){const n=e(this).data("field-id");e("#"+n).val(t),this.refresh()},getProps(){const t=e(this).data("field-id");return{value:Number(e("#"+t).val()),onChange:this.handleChange.bind(this)}},onunmatch(){this.getRoot().unmount()}})}))},689:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n(648);const i={props(e){const{data:{error:t,readLinkTypes:n,loading:r}}=e,i=t&&t.graphQLErrors&&t.graphQLErrors.map((e=>e.message));return{loading:r,types:n?n.reduce(((e,t)=>({...e,[t.key]:t})),{}):{},graphQLErrors:i}}},{READ:o}=r.graphqlTemplates;var l={apolloConfig:i,templateName:o,pluralName:"LinkTypes",pagination:!1,params:{keys:"[ID]"},args:{root:{keys:"keys"}},fields:["key","title"]};t.default=l},686:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,i=(r=n(86))&&r.__esModule?r:{default:r};var o=i.default.shape({key:i.default.string.isRequired,title:i.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 i=t[r];if(void 0!==i)return i.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 diff --git a/client/lang/en.js b/client/lang/en.js index e01d5970..3480f12e 100644 --- a/client/lang/en.js +++ b/client/lang/en.js @@ -6,74 +6,10 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') { } } else { ss.i18n.addDictionary('en', { - "AssetAdmin.ADD_FOLDER_BUTTON": "Add folder", - "AssetAdmin.BACK": "Back", - "AssetAdmin.BACK_DESCRIPTION": "Navigate up a level", - "AssetAdmin.BULK_ACTIONS_CONFIRM": "Are you sure you want to %s these files?", - "AssetAdmin.BULK_ACTIONS_DELETE": "Delete", - "AssetAdmin.BULK_ACTIONS_DELETE_CONFIRM": "Are you sure you want to delete these files?", - "AssetAdmin.BULK_ACTIONS_DELETE_FAIL": "%s folders/files were successfully archived, but %s files were not able to be archived.", - "AssetAdmin.BULK_ACTIONS_DELETE_FOLDER": "These folders contain files which are currently in use, you must move or delete their contents before you can delete the folder.", - "AssetAdmin.BULK_ACTIONS_DELETE_MULTI_CONFIRM": "There are %s files currently in use, are you sure you want to delete these files?", - "AssetAdmin.BULK_ACTIONS_DELETE_SINGLE_CONFIRM": "This file is currently in use in %s places, are you sure you want to delete it?", - "AssetAdmin.BULK_ACTIONS_DELETE_SUCCESS": "%s folders/files were successfully archived.", - "AssetAdmin.BULK_ACTIONS_PLACEHOLDER": "Select an action...", - "AssetAdmin.CANCEL": "Cancel", - "AssetAdmin.CONFIRMDELETE": "Are you sure you want to delete this record?", - "AssetAdmin.CREATED": "First uploaded", - "AssetAdmin.DELETE": "Delete", - "AssetAdmin.DIM": "Dimensions", - "AssetAdmin.DROPZONE_CANCEL_UPLOAD": "Cancel upload", - "AssetAdmin.DROPZONE_CANCEL_UPLOAD_CONFIRMATION": "Are you sure you want to cancel this upload?", - "AssetAdmin.DROPZONE_DEFAULT_MESSAGE": "Drop files here to upload", - "AssetAdmin.DROPZONE_FAILED_UPLOAD": "Failed to upload file", - "AssetAdmin.DROPZONE_FALLBACK_MESSAGE": "Your browser does not support drag'n'drop file uploads.", - "AssetAdmin.DROPZONE_FALLBACK_TEXT": "Please use the fallback form below to upload your files like in the olden days.", - "AssetAdmin.DROPZONE_FILE_TOO_BIG": "File is too big. Max filesize: %sMiB.", - "AssetAdmin.DROPZONE_INVALID_FILE_TYPE": "You can't upload files of this type.", - "AssetAdmin.DROPZONE_MAX_FILES_EXCEEDED": "You can not upload any more files.", - "AssetAdmin.DROPZONE_REMOVE_FILE": "Remove file", - "AssetAdmin.DROPZONE_RESPONSE_ERROR": "Server responded with an error.", - "AssetAdmin.DROPZONE_SUCCESS_UPLOAD": "File uploaded", - "AssetAdmin.DROPZONE_UPLOAD": "Upload", - "AssetAdmin.EDIT": "Edit", - "AssetAdmin.FILENAME": "Filename", - "AssetAdmin.FILES": "Files", - "AssetAdmin.FILE_MISSING": "File cannot be found", - "AssetAdmin.FILTER_DATE_ASC": "oldest", - "AssetAdmin.FILTER_DATE_DESC": "newest", - "AssetAdmin.FILTER_TITLE_ASC": "title a-z", - "AssetAdmin.FILTER_TITLE_DESC": "title z-a", - "AssetAdmin.JOIN": ",", - "AssetAdmin.JOINLAST": "and", - "AssetAdmin.LASTEDIT": "Last changed", - "AssetAdmin.LOADMORE": "Load more", - "AssetAdmin.NOITEMSFOUND": "No items found", - "AssetAdmin.PROMPTFOLDERNAME": "Please enter a folder name (or blank to cancel)", - "AssetAdmin.REPlACE_FILE_SUCCESS": "Upload successful, the file will be replaced when you Save.", - "AssetAdmin.SAVE": "Save", - "AssetAdmin.SEARCHCLEARRESULTS": "Clear search", - "AssetAdmin.SEARCHRESULTS": "Search results", - "AssetAdmin.SEARCHRESULTSMESSAGE": "Search results {parts}", - "AssetAdmin.SEARCHRESULTSMESSAGECATEGORY": "categorised as '{appCategory}'", - "AssetAdmin.SEARCHRESULTSMESSAGEEDITEDBETWEEN": "last edited between '{lastEditedFrom}' and '{lastEditedTo}'", - "AssetAdmin.SEARCHRESULTSMESSAGEEDITEDFROM": "last edited from '{lastEditedFrom}'", - "AssetAdmin.SEARCHRESULTSMESSAGEEDITEDTO": "last edited before '{lastEditedTo}'", - "AssetAdmin.SEARCHRESULTSMESSAGEKEYWORDS": "with keywords '{name}'", - "AssetAdmin.SEARCHRESULTSMESSAGELIMIT": "limited to the folder '{folder}'", - "AssetAdmin.SELECT": "Select", - "AssetAdmin.SIZE": "Size", - "AssetAdmin.TITLE": "Title", - "AssetAdmin.TYPE": "File type", - "AssetAdmin.URL": "URL", - "AssetAdmin.ADD_FILES": "Add from files", - "AssetAdmin.BROWSE": "Browse", - "AssetAdmin.EMPTY": "No files", - "AssetAdmin.OR": "or", - "AssetAdmin.ERROR_OEMBED_REMOTE": "Embed is only compatible with remote files", - "AssetAdmin.CreateTitle": "Insert new media from the web", - "AssetAdmin.EditTitle": "Media from the web", - "AssetAdmin.NEXT": "Next", - "AssetAdmin.PREVIOUS": "Previous" -}); + "LinkField.SAVE_SUCCESS": "Saved link", + "LinkField.CONFIRM_DELETE": "Deleted link", + "LinkField.DELETE_ERROR": "Failed to delete link", + "LinkField.ADD_LINK": "Add Link", + "LinkField.CLEAR": "Clear" + }); } \ No newline at end of file diff --git a/client/lang/src/en.json b/client/lang/src/en.json index 66a3d462..91105122 100644 --- a/client/lang/src/en.json +++ b/client/lang/src/en.json @@ -1,75 +1,7 @@ { - "AssetAdmin.ADD_FOLDER_BUTTON": "Add folder", - "AssetAdmin.BACK": "Back", - "AssetAdmin.BACK_DESCRIPTION": "Navigate up a level", - "AssetAdmin.BULK_ACTIONS_CONFIRM": "Are you sure you want to %s these files?", - "AssetAdmin.BULK_ACTIONS_DELETE": "Delete", - "AssetAdmin.BULK_ACTIONS_DELETE_SINGLE_ITEM_CONFIRM": "Are you sure you want to delete this file/folder?", - "AssetAdmin.BULK_ACTIONS_DELETE_MULTIPLE_ITEMS_CONFIRM": "Are you sure you want to delete these files/folders?", - "AssetAdmin.BULK_ACTIONS_DELETE_MULTIPLE_ITEMS_IN_USE_CONFIRM": "%s item(s) are currently in use in %s place(s). Are you sure you want to delete them?", - "AssetAdmin.BULK_ACTIONS_DELETE_SINGLE_FILE_IN_USE_CONFIRM": "This file is currently in use in %s place(s). Are you sure you want to delete it?", - "AssetAdmin.BULK_ACTIONS_DELETE_MULTIPLE_FILES_IN_USE_CONFIRM": "%s of these files are currently used in %s place(s). Are you sure you want to delete them?", - "AssetAdmin.BULK_ACTIONS_DELETE_SINGLE_FOLDER_IN_USE_CONFIRM": "This folder contains file(s) that are currently used in %s place(s). Are you sure you want to delete it?", - "AssetAdmin.BULK_ACTIONS_DELETE_MULTIPLE_FOLDERS_IN_USE_CONFIRM": "%s of these folders contain file(s) that are currently used in %s place(s). Are you sure you want to delete them?", - "AssetAdmin.BULK_ACTIONS_DELETE_WARNING": "Ensure files are removed from content areas prior to deleting them, otherwise they will appear as broken links.", - "AssetAdmin.BULK_ACTIONS_DELETE_FAIL": "%s folders/files were successfully archived, but %s files were not able to be archived.", - "AssetAdmin.BULK_ACTIONS_DELETE_SUCCESS": "%s folders/files were successfully archived.", - "AssetAdmin.BULK_ACTIONS_PLACEHOLDER": "Select an action...", - "AssetAdmin.CANCEL": "Cancel", - "AssetAdmin.CONFIRMDELETE": "Are you sure you want to delete this record?", - "AssetAdmin.CREATED": "First uploaded", - "AssetAdmin.DELETE": "Delete", - "AssetAdmin.DIM": "Dimensions", - "AssetAdmin.DROPZONE_CANCEL_UPLOAD": "Cancel upload", - "AssetAdmin.DROPZONE_CANCEL_UPLOAD_CONFIRMATION": "Are you sure you want to cancel this upload?", - "AssetAdmin.DROPZONE_DEFAULT_MESSAGE": "Drop files here to upload", - "AssetAdmin.DROPZONE_FAILED_UPLOAD": "Failed to upload file", - "AssetAdmin.DROPZONE_FALLBACK_MESSAGE": "Your browser does not support drag'n'drop file uploads.", - "AssetAdmin.DROPZONE_FALLBACK_TEXT": "Please use the fallback form below to upload your files like in the olden days.", - "AssetAdmin.DROPZONE_FILE_TOO_BIG": "File is too big. Max filesize: %sMiB.", - "AssetAdmin.DROPZONE_INVALID_FILE_TYPE": "You can't upload files of this type.", - "AssetAdmin.DROPZONE_MAX_FILES_EXCEEDED": "You can not upload any more files.", - "AssetAdmin.DROPZONE_REMOVE_FILE": "Remove file", - "AssetAdmin.DROPZONE_RESPONSE_ERROR": "Server responded with an error.", - "AssetAdmin.DROPZONE_SUCCESS_UPLOAD": "File uploaded", - "AssetAdmin.DROPZONE_UPLOAD": "Upload", - "AssetAdmin.EDIT": "Edit", - "AssetAdmin.FILENAME": "Filename", - "AssetAdmin.FILES": "Files", - "AssetAdmin.FILE_MISSING": "File cannot be found", - "AssetAdmin.FILTER_DATE_ASC": "oldest", - "AssetAdmin.FILTER_DATE_DESC": "newest", - "AssetAdmin.FILTER_TITLE_ASC": "title a-z", - "AssetAdmin.FILTER_TITLE_DESC": "title z-a", - "AssetAdmin.JOIN": ",", - "AssetAdmin.JOINLAST": "and", - "AssetAdmin.LASTEDIT": "Last changed", - "AssetAdmin.LOADMORE": "Load more", - "AssetAdmin.NOITEMSFOUND": "No items found", - "AssetAdmin.PROMPTFOLDERNAME": "Please enter a folder name (or blank to cancel)", - "AssetAdmin.REPlACE_FILE_SUCCESS": "Upload successful, the file will be replaced when you Save.", - "AssetAdmin.SAVE": "Save", - "AssetAdmin.SEARCHCLEARRESULTS": "Clear search", - "AssetAdmin.SEARCHRESULTS": "Search results", - "AssetAdmin.SEARCHRESULTSMESSAGE": "Search results {parts}", - "AssetAdmin.SEARCHRESULTSMESSAGECATEGORY": "categorised as '{appCategory}'", - "AssetAdmin.SEARCHRESULTSMESSAGEEDITEDBETWEEN": "last edited between '{lastEditedFrom}' and '{lastEditedTo}'", - "AssetAdmin.SEARCHRESULTSMESSAGEEDITEDFROM": "last edited from '{lastEditedFrom}'", - "AssetAdmin.SEARCHRESULTSMESSAGEEDITEDTO": "last edited before '{lastEditedTo}'", - "AssetAdmin.SEARCHRESULTSMESSAGEKEYWORDS": "with keywords '{name}'", - "AssetAdmin.SEARCHRESULTSMESSAGELIMIT": "limited to the folder '{folder}'", - "AssetAdmin.SELECT": "Select", - "AssetAdmin.SIZE": "Size", - "AssetAdmin.TITLE": "Title", - "AssetAdmin.TYPE": "File type", - "AssetAdmin.URL": "URL", - "AssetAdmin.ADD_FILES": "Add from files", - "AssetAdmin.BROWSE": "Browse", - "AssetAdmin.EMPTY": "No files", - "AssetAdmin.OR": "or", - "AssetAdmin.ERROR_OEMBED_REMOTE": "Embed is only compatible with remote files", - "AssetAdmin.CreateTitle": "Insert new media from the web", - "AssetAdmin.EditTitle": "Media from the web", - "AssetAdmin.NEXT": "Next", - "AssetAdmin.PREVIOUS": "Previous" + "LinkField.SAVE_SUCCESS": "Saved link", + "LinkField.CONFIRM_DELETE": "Deleted link", + "LinkField.DELETE_ERROR": "Failed to delete link", + "LinkField.ADD_LINK": "Add Link", + "LinkField.CLEAR": "Clear" } diff --git a/client/src/boot/registerComponents.js b/client/src/boot/registerComponents.js index 2007dc08..12fc4d24 100644 --- a/client/src/boot/registerComponents.js +++ b/client/src/boot/registerComponents.js @@ -1,4 +1,3 @@ - /* eslint-disable */ import Injector from 'lib/Injector'; import LinkPicker from 'components/LinkPicker/LinkPicker'; diff --git a/client/src/components/LinkField/LinkField.js b/client/src/components/LinkField/LinkField.js index 8810af03..b6d1d77b 100644 --- a/client/src/components/LinkField/LinkField.js +++ b/client/src/components/LinkField/LinkField.js @@ -1,3 +1,4 @@ +/* eslint-disable */ import React, { useState, useEffect } from 'react'; import { bindActionCreators, compose } from 'redux'; import { connect } from 'react-redux'; @@ -8,6 +9,7 @@ import * as toastsActions from 'state/toasts/ToastsActions'; import backend from 'lib/Backend'; import Config from 'lib/Config'; import PropTypes from 'prop-types'; +import i18n from 'i18n'; // section used in window.ss config const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController'; @@ -49,7 +51,12 @@ const LinkField = ({ value, onChange, types, actions }) => { onChange(valueFromSchemaResponse); // success toast - actions.toasts.success('Saved link'); + actions.toasts.success( + i18n._t( + 'LinkField.SAVE_SUCCESS', + 'Saved link', + ) + ); } return Promise.resolve(); @@ -63,10 +70,20 @@ const LinkField = ({ value, onChange, types, actions }) => { // CSRF token 'X-SecurityID' headers needs to be present for destructive requests backend.delete(endpoint, {}, { 'X-SecurityID': Config.get('SecurityID') }) .then(() => { - actions.toasts.success('Deleted link'); + actions.toasts.success( + i18n._t( + 'LinkField.DELETE_SUCCESS', + 'Deleted link', + ) + ); }) .catch(() => { - actions.toasts.error('Failed to delete link'); + actions.toasts.error( + i18n._t( + 'LinkField.DELETE_ERROR', + 'Failed to delete link', + ) + ); }); // update component state diff --git a/client/src/components/LinkPicker/LinkPickerMenu.js b/client/src/components/LinkPicker/LinkPickerMenu.js index a3eb3c70..3413d4c4 100644 --- a/client/src/components/LinkPicker/LinkPickerMenu.js +++ b/client/src/components/LinkPicker/LinkPickerMenu.js @@ -15,7 +15,7 @@ const LinkPickerMenu = ({ types, onSelect }) => { toggle={toggle} className="link-picker__menu" > - {i18n._t('Link.ADD_LINK', 'Add Link')} + {i18n._t('LinkField.ADD_LINK', 'Add Link')} {types.map(({key, title}) => onSelect(key)}>{title} diff --git a/client/src/components/LinkPicker/LinkPickerTitle.js b/client/src/components/LinkPicker/LinkPickerTitle.js index c282fad7..d925f8bc 100644 --- a/client/src/components/LinkPicker/LinkPickerTitle.js +++ b/client/src/components/LinkPicker/LinkPickerTitle.js @@ -23,7 +23,7 @@ const LinkPickerTitle = ({ title, description, typeTitle, onClear, onClick }) => - + ); diff --git a/lang/en.yml b/lang/en.yml new file mode 100644 index 00000000..33d421e4 --- /dev/null +++ b/lang/en.yml @@ -0,0 +1,34 @@ +en: + LinkField: + UPDATELINK: 'Update link' + INVALIDID': 'Invalid ID' + UNAUTHORIZED: 'Unauthorized' + INVALIDTYPEKEY: 'Invalid typeKey' + INVALIDTOKEN: 'Invalid CSRF token' + EMPTYDATA: 'Empty data' + BADDATA: 'Bad data' + CREATELINK: 'Create link' + KEYSARENOTARRAY: 'If keys is provdied, it must be an array' + LINKTYPETITLE: 'Link Type' + INVALIDJSON: '"{class}": Decoding json string failred with "{error}"' + INVALIDDATATOARRAY: '"{class}": Could not convert $data to an array.' + DATAHASNOTYPEKEY: '"{class}": $data does not have a typeKey.' + NOTREGISTEREDLINKTYPE: '"{class}": "{typekey}" is not a registered Link Type.' + TITLEDESCRIPTION: 'Auto generated from Page title if left blank' + LINKFIELDTITLE: 'Title' + PAGEFIELDTITLE: 'Page' + QUERYFIELDTITLE: 'Query string' + ANCHORFIELDTITLE: 'Anchor' + OPENINNEWTITLE: 'Open in new window?' + EXTERNALURLFIELD: 'External url' + EMAILFIELD: 'Email address' + FILEFIELD: 'File' + PHONEFIELD: 'Phone' + QUERYSTRINGDESCRIPTION: 'Do not prepend "?". EG: "option1=value&option2=value2"' + ANCHORDESCRIPTION: 'Do not prepend "#". Anchor suggestions will be displayed once the linked page is attached.' + VERSIONEDSTATUSMISMATCH: 'Linkable and LinkField do not have matching Versioned applications. Make sure that both are either un-Versioned or Versioned' + NOTHINGTOPROCESS: "Nothing to process for {table}\r\n" + PROCESSINGTABLE: "Processing {table}\r\n" + RECORDSINSERTED: "{numrecords} records inserted, finished processing {table}\r\n" + NOCLASSNAME: '"{class}": All types should reference a valid classname' + INVALIDTYPENAME: '"{class}": {typename} is not a valid link type' diff --git a/src/Controllers/LinkFieldController.php b/src/Controllers/LinkFieldController.php index 4c950ae7..eaa02623 100644 --- a/src/Controllers/LinkFieldController.php +++ b/src/Controllers/LinkFieldController.php @@ -64,17 +64,17 @@ public function linkForm(): Form if ($id) { $link = Link::get()->byID($id); if (!$link) { - $this->jsonError(404, 'Invalid ID'); + $this->jsonError(404, _t('LinkField.INVALIDID', 'Invalid ID')); } $operation = 'edit'; if (!$link->canView()) { - $this->jsonError(403, 'Unauthorized'); + $this->jsonError(403, _t('LinkField.UNAUTHORIZED', 'Unauthorized')); } } else { $typeKey = $this->typeKeyFromRequest(); $link = Registry::create()->byKey($typeKey); if (!$link) { - $this->jsonError(404, 'Invalid typeKey'); + $this->jsonError(404, _t('LinkField.INVALIDTYPEKEY', 'Invalid typeKey')); } $operation = 'create'; } @@ -89,7 +89,7 @@ public function linkData(): HTTPResponse { $link = $this->linkFromRequest(); if (!$link->canView()) { - $this->jsonError(403, 'Unauthorized'); + $this->jsonError(403, _t('LinkField.UNAUTHORIZED', 'Unauthorized')); } $response = $this->getResponse(); $response->addHeader('Content-type', 'application/json'); @@ -107,11 +107,11 @@ public function linkDelete(): HTTPResponse { $link = $this->linkFromRequest(); if (!$link->canDelete()) { - $this->jsonError(403, 'Unauthorized'); + $this->jsonError(403, _t('LinkField.UNAUTHORIZED', 'Unauthorized')); } // Check security token on destructive operation if (!SecurityToken::inst()->checkRequest($this->getRequest())) { - $this->jsonError(400, 'Invalid CSRF token'); + $this->jsonError(400, _t('LinkField.INVALIDTOKEN', 'Invalid CSRF token')); } // delete() will also delete any published version immediately $link->delete(); @@ -138,7 +138,7 @@ public function getLinkForm(): Form public function save(array $data, Form $form): HTTPResponse { if (empty($data)) { - $this->jsonError(400, 'Empty data'); + $this->jsonError(400, _t('LinkField.EMPTYDATA', 'Empty data')); } /** @var Link $link */ @@ -148,10 +148,10 @@ public function save(array $data, Form $form): HTTPResponse $operation = 'edit'; $link = Link::get()->byID($id); if (!$link) { - $this->jsonErorr(404, 'Invalid ID'); + $this->jsonErorr(404, _t('LinkField.INVALIDID', 'Invalid ID')); } if (!$link->canEdit()) { - $this->jsonError(403, 'Unauthorized'); + $this->jsonError(403, _t('LinkField.UNAUTHORIZED', 'Unauthorized')); } } else { // Creating a new Link @@ -159,17 +159,17 @@ public function save(array $data, Form $form): HTTPResponse $typeKey = $this->typeKeyFromRequest(); $className = Registry::create()->list()[$typeKey] ?? ''; if (!$className) { - $this->jsonError(404, 'Invalid typeKey'); + $this->jsonError(404, _t('LinkField.INVALIDTYPEKEY', 'Invalid typeKey')); } $link = $className::create(); if (!$link->canCreate()) { - $this->jsonError(403, 'Unauthorized'); + $this->jsonError(403, _t('LinkField.UNAUTHORIZED', 'Unauthorized')); } } // Ensure that ItemID url param matches the ID in the form data if (isset($data['ID']) && ((int) $data['ID'] !== $id)) { - $this->jsonError(400, 'Bad data'); + $this->jsonError(400, _t('LinkField.BADDATA', 'Bad data')); } // Update DataObject from form data @@ -226,7 +226,9 @@ private function createLinkForm(Link $link, string $operation): Form $form->setFormAction($this->Link("linkForm/$id?typeKey=$typeKey")); // Add save action button - $title = $id ? 'Update link' : 'Create link'; // todo: _t() + $title = $id + ? _t('LinkField.UPDATELINK', 'Update link') + : _t('LinkField.CREATELINK', 'Create link'); $actions = FieldList::create([ FormAction::create('save', $title) ->setSchemaData(['data' => ['buttonStyle' => 'primary']]), @@ -263,11 +265,11 @@ private function linkFromRequest(): Link { $itemID = (int) $this->itemIDFromRequest(); if (!$itemID) { - $this->jsonError(404, 'Invalid ID'); + $this->jsonError(404, _t('LinkField.INVALIDID', 'Invalid ID')); } $link = Link::get()->byID($itemID); if (!$link) { - $this->jsonError(404, 'Invalid ID'); + $this->jsonError(404, _t('LinkField.INVALIDID', 'Invalid ID')); } return $link; } @@ -280,7 +282,7 @@ private function itemIDFromRequest(): string $request = $this->getRequest(); $itemID = (string) $request->param('ItemID'); if (!ctype_digit($itemID)) { - $this->jsonError(404, 'Invalid ID'); + $this->jsonError(404, _t('LinkField.INVALIDID', 'Invalid ID')); } return $itemID; } @@ -293,7 +295,7 @@ private function typeKeyFromRequest(): string $request = $this->getRequest(); $typeKey = (string) $request->getVar('typeKey'); if (strlen($typeKey) === 0 || !preg_match('#^[a-z\-]+$#', $typeKey)) { - $this->jsonError(404, 'Invalid typeKey'); + $this->jsonError(404, _t('LinkField.INVALIDTYPEKEY', 'Invalid typeKey')); } return $typeKey; } diff --git a/src/GraphQL/LinkTypeResolver.php b/src/GraphQL/LinkTypeResolver.php index 4f1b1b69..7900069b 100644 --- a/src/GraphQL/LinkTypeResolver.php +++ b/src/GraphQL/LinkTypeResolver.php @@ -13,7 +13,7 @@ class LinkTypeResolver extends Resolver public static function resolve($obj, $args = [], $context = [], ?ResolveInfo $info = null) { if (isset($args['keys']) && !is_array($args['keys'])) { - throw new InvalidArgumentException('If `keys` is provdied, it must be an array'); + throw new InvalidArgumentException(_t('LinkField.KEYSARENOTARRAY', 'If `keys` is provdied, it must be an array')); } $types = Registry::singleton()->list(); diff --git a/src/Models/EmailLink.php b/src/Models/EmailLink.php index c41143ee..d8c65f37 100644 --- a/src/Models/EmailLink.php +++ b/src/Models/EmailLink.php @@ -18,19 +18,22 @@ class EmailLink extends Link 'Email' => 'Varchar(255)', ]; - public function getDescription(): string - { - return $this->Email ?: ''; - } - public function getCMSFields(): FieldList { $this->beforeUpdateCMSFields(static function (FieldList $fields) { - $fields->replaceField('Email', EmailField::create('Email')); + $fields->replaceField('Email', EmailField::create( + 'Email', + _t('LinkField.EMAILFIELD', 'Email address'), + )); }); return parent::getCMSFields(); } + public function getDescription(): string + { + return $this->Email ?: ''; + } + public function getURL(): string { return $this->Email ? sprintf('mailto:%s', $this->Email) : ''; diff --git a/src/Models/ExternalLink.php b/src/Models/ExternalLink.php index 732d620e..e93755b9 100644 --- a/src/Models/ExternalLink.php +++ b/src/Models/ExternalLink.php @@ -2,6 +2,8 @@ namespace SilverStripe\LinkField\Models; +use SilverStripe\Forms\FieldList; + /** * A link to an external URL. * @@ -15,6 +17,15 @@ class ExternalLink extends Link 'ExternalUrl' => 'Varchar', ]; + public function getCMSFields(): FieldList + { + $this->beforeUpdateCMSFields(static function (FieldList $fields) { + $linkField = $fields->dataFieldByName('ExternalUrl'); + $linkField->setTitle(_t('LinkField.EXTERNALURLFIELD', 'External url')); + }); + return parent::getCMSFields(); + } + public function getDescription(): string { return $this->ExternalUrl ?: ''; diff --git a/src/Models/FileLink.php b/src/Models/FileLink.php index 21fbd006..e3dda2f4 100644 --- a/src/Models/FileLink.php +++ b/src/Models/FileLink.php @@ -3,6 +3,7 @@ namespace SilverStripe\LinkField\Models; use SilverStripe\Assets\File; +use SilverStripe\Forms\FieldList; class FileLink extends Link { @@ -12,6 +13,15 @@ class FileLink extends Link 'File' => File::class, ]; + public function getCMSFields(): FieldList + { + $this->beforeUpdateCMSFields(static function (FieldList $fields) { + $linkField = $fields->dataFieldByName('File'); + $linkField->setTitle(_t('LinkField.FILEFIELD', 'File')); + }); + return parent::getCMSFields(); + } + public function getDescription(): string { return $this->File()?->getFilename() ?? ''; diff --git a/src/Models/Link.php b/src/Models/Link.php index 6657da9c..8b59b8cc 100644 --- a/src/Models/Link.php +++ b/src/Models/Link.php @@ -66,12 +66,22 @@ public function getCMSFields(): FieldList $this->beforeUpdateCMSFields(function (FieldList $fields) { $linkTypes = $this->getLinkTypes(); + $titleField = $fields->dataFieldByName('Title'); + $titleField->setTitle(_t('LinkField.LINKFIELDTITLE', 'Title')); + + $openInNewField = $fields->dataFieldByName('OpenInNew'); + $openInNewField->setTitle(_t('LinkField.OPENINNEWTITLE', 'Open in new window?')); + if (static::class === self::class) { // Add a link type selection field for generic links $fields->addFieldsToTab( 'Root.Main', [ - $linkTypeField = DropdownField::create('LinkType', 'Link Type', $linkTypes), + $linkTypeField = DropdownField::create( + 'LinkType', + _t('LinkField.LINKTYPETITLE', 'Link Type'), + $linkTypes + ), ], 'Title' ); @@ -129,30 +139,64 @@ function setData($data): Link $data = json_decode($data, true); if (json_last_error() !== JSON_ERROR_NONE) { - throw new InvalidArgumentException(sprintf( - '%s: Decoding json string failred with "%s"', - static::class, - json_last_error_msg() - )); + throw new InvalidArgumentException( + _t( + 'LinkField.INVALIDJSON', + '"{class}": Decoding json string failred with "{error}"', + [ + 'class' => static::class, + 'error' => json_last_error_msg(), + ], + sprintf( + '"%s": Decoding json string failred with "%s"', + static::class, + json_last_error_msg(), + ), + ), + ); } } elseif ($data instanceof Link) { $data = $data->jsonSerialize(); } if (!is_array($data)) { - throw new InvalidArgumentException(sprintf('%s: Could not convert $data to an array.', static::class)); + throw new InvalidArgumentException( + _t( + 'LinkField.INVALIDDATATOARRAY', + '"{class}": Could not convert $data to an array.', + ['class' => static::class], + sprintf('%s: Could not convert $data to an array.', static::class), + ), + ); } $typeKey = $data['typeKey'] ?? null; if (!$typeKey) { - throw new InvalidArgumentException(sprintf('%s: $data does not have a typeKey.', static::class)); + throw new InvalidArgumentException( + _t( + 'LinkField.DATAHASNOTYPEKEY', + '"{class}": $data does not have a typeKey.', + ['class' => static::class], + sprintf('%s: $data does not have a typeKey.', static::class), + ), + ); } $type = Registry::singleton()->byKey($typeKey); if (!$type) { - throw new InvalidArgumentException(sprintf('%s: %s is not a registered Link Type.', static::class, $typeKey)); + throw new InvalidArgumentException( + _t( + 'LinkField.NOTREGISTEREDLINKTYPE', + '"{class}": "{typekey}" is not a registered Link Type.', + [ + 'class' => static::class, + 'typekey' => $typeKey + ], + sprintf('"%s": "%s" is not a registered Link Type.', static::class, $typeKey), + ), + ); } $jsonData = $this; diff --git a/src/Models/PhoneLink.php b/src/Models/PhoneLink.php index df2d5b8d..8915dc74 100644 --- a/src/Models/PhoneLink.php +++ b/src/Models/PhoneLink.php @@ -2,6 +2,8 @@ namespace SilverStripe\LinkField\Models; +use SilverStripe\Forms\FieldList; + /** * A link to a phone number */ @@ -13,6 +15,15 @@ class PhoneLink extends Link 'Phone' => 'Varchar(255)', ]; + public function getCMSFields(): FieldList + { + $this->beforeUpdateCMSFields(static function (FieldList $fields) { + $linkField = $fields->dataFieldByName('Phone'); + $linkField->setTitle(_t('LinkField.PHONEFIELD', 'Phone')); + }); + return parent::getCMSFields(); + } + public function getDescription(): string { return $this->Phone ?: ''; diff --git a/src/Models/SiteTreeLink.php b/src/Models/SiteTreeLink.php index 5f8b70d2..4ea8b3e0 100644 --- a/src/Models/SiteTreeLink.php +++ b/src/Models/SiteTreeLink.php @@ -46,13 +46,18 @@ public function getCMSFields(): FieldList ]); $titleField = $fields->dataFieldByName('Title'); - $titleField?->setDescription('Auto generated from Page title if left blank'); + $titleField?->setDescription( + _t( + 'LinkField.TITLEDESCRIPTION', + 'Auto generated from Page title if left blank', + ), + ); $fields->insertAfter( 'Title', TreeDropdownField::create( 'PageID', - 'Page', + _t('LinkField.PAGEFIELDTITLE', 'Page'), SiteTree::class, 'ID', 'TreeTitle' @@ -61,18 +66,32 @@ public function getCMSFields(): FieldList $fields->insertAfter( 'PageID', - $queryStringField = TextField::create('QueryString') + $queryStringField = TextField::create( + 'QueryString', + _t('LinkField.QUERYFIELDTITLE', 'Query string'), + ) ); - $queryStringField->setDescription('Do not prepend "?". EG: "option1=value&option2=value2"'); + $queryStringField->setDescription( + _t( + 'LinkField.QUERYSTRINGDESCRIPTION', + 'Do not prepend "?". EG: "option1=value&option2=value2"', + ), + ); $fields->insertAfter( 'QueryString', - $anchorField = AnchorSelectorField::create('Anchor') + $anchorField = AnchorSelectorField::create( + 'Anchor', + _t('LinkField.ANCHORFIELDTITLE', 'Anchor') + ) ); $anchorField->setDescription( - 'Do not prepend "#". Anchor suggestions will be displayed once the linked page is attached.' + _t( + 'LinkField.ANCHORDESCRIPTION', + 'Do not prepend "#". Anchor suggestions will be displayed once the linked page is attached.', + ), ); }); diff --git a/src/Tasks/LinkableMigrationTask.php b/src/Tasks/LinkableMigrationTask.php index a3914992..def818d3 100644 --- a/src/Tasks/LinkableMigrationTask.php +++ b/src/Tasks/LinkableMigrationTask.php @@ -171,8 +171,11 @@ public function run($request): void // Check that we have matching Versioned states between Linkable and LinkField if (!$this->versionedStatusMatches()) { throw new Exception( - 'Linkable and LinkField do not have matching Versioned applications. Make sure that both are' - . ' either un-Versioned or Versioned' + _t( + 'LinkField.VERSIONEDSTATUSMISMATCH', + 'Linkable and LinkField do not have matching Versioned applications. Make sure that both are' + . ' either un-Versioned or Versioned' + ), ); } @@ -199,12 +202,22 @@ public function run($request): void // Nothing to see here if ($linkableResults->numRecords() === 0) { - echo sprintf("Nothing to process for `%s`\r\n", $table); + echo _t( + 'LinkField.NOTHINGTOPROCESS', + "Nothing to process for `{table}`\r\n", + ['table' => $table], + sprintf("Nothing to process for `%s`\r\n", $table) + ); continue; } - echo sprintf("Processing `%s`\r\n", $table); + echo _t( + 'LinkField.PROCESSINGTABLE', + "Processing `{table}`\r\n", + ['table' => $table], + sprintf("Processing `%s`\r\n", $table) + ); // Loop through each DB record foreach ($linkableResults as $linkableData) { @@ -234,7 +247,15 @@ public function run($request): void } } - echo sprintf("%d records inserted, finished processing `%s`\r\n", $linkableResults->numRecords(), $table); + echo _t( + 'LinkField.RECORDSINSERTED', + "{numrecords} records inserted, finished processing `{table}`\r\n", + [ + 'numrecords' => $linkableResults->numRecords(), + 'table' => $table, + ], + sprintf("%d records inserted, finished processing `%s`\r\n", $linkableResults->numRecords(), $table) + ); } } diff --git a/src/Type/Registry.php b/src/Type/Registry.php index 50d96f94..6c1046d4 100644 --- a/src/Type/Registry.php +++ b/src/Type/Registry.php @@ -96,14 +96,31 @@ private function definitionToType(array $def): Link $className = $def['classname'] ?? null; if (!$className) { - throw new LogicException(sprintf('%s: All types should reference a valid classname', static::class)); + throw new LogicException( + _t( + 'LinkField.NOCLASSNAME', + '"{class}": All types should reference a valid classname', + ['class' => static::class], + sprintf('%s: All types should reference a valid classname', static::class), + ), + ); } /** @var Link $type */ $type = Injector::inst()->get($className); if (!$type instanceof Link) { - throw new LogicException(sprintf('%s: %s is not a valid link type', static::class, $className)); + throw new LogicException( + _t( + 'LinkField.INVALIDTYPENAME', + '"{class}": {typename} is not a valid link type', + [ + 'class' => static::class, + 'typename' => $className, + ], + sprintf('%s: %s is not a valid link type', static::class, $className), + ), + ); } return $type;