diff --git a/client/dist/js/userforms-cms.js b/client/dist/js/userforms-cms.js index 1ce672a..1988e4b 100644 --- a/client/dist/js/userforms-cms.js +++ b/client/dist/js/userforms-cms.js @@ -1 +1 @@ -!function(e){function i(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,i),s.l=!0,s.exports}var t={};i.m=e,i.c=t,i.i=function(e){return e},i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,i){return Object.prototype.hasOwnProperty.call(e,i)},i.p="",i(i.s="./client/src/bundles/bundle-cms.js")}({"./client/src/bundles/FieldEditor.js":function(e,i,t){"use strict";var n=t(0);(function(e){return e&&e.__esModule?e:{default:e}})(n).default.entwine("ss",function(e){var i=null;e(".uf-field-editor .ss-gridfield-items").entwine({onmatch:function(){var t=0,n=0,s=e(".uf-field-editor .ss-gridfield-buttonrow").addClass("sticky-buttons"),r=e(".cms-content-header.north").first().height()+parseInt(e(".sticky-buttons").css("padding-top"),10),o=e(".uf-field-editor");this._super(),this.find(".ss-gridfield-item").each(function(i,s){switch(e(s).data("class")){case"SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFormStep":return void(n=0);case"SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFieldGroup":n+=1,t=n;break;case"SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFieldGroupEnd":t=n,n-=1;break;default:t=n}e(s).toggleClass("infieldgroup",t>0);for(var r=1;r<=5;r++)e(s).toggleClass("infieldgroup-level-"+r,t>=r)}),i=setInterval(function(){var e=o.offset().top;s.width("100%"),e>r||0===e?s.removeClass("sticky-buttons"):s.addClass("sticky-buttons")},300)},onunmatch:function(){this._super(),clearInterval(i)}}),e(".uf-field-editor .ss-gridfield-buttonrow .action").entwine({onclick:function(e){this._super(e),this.trigger("addnewinline")}}),e(".uf-field-editor").entwine({onmatch:function(){var i=this;this._super(),this.on("addnewinline",function(){i.one("reload",function(){var t=i.find(".ss-gridfield-item").last(),n=null;"SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFieldGroupEnd"===t.attr("data-class")?(n=t,n.prev().find(".col-Title input").focus(),t=n.add(n.prev()),n.css("visibility","hidden")):t.find(".col-Title input").focus(),t.addClass("flashBackground"),e(".cms-content-fields").scrollTop(e(".cms-content-fields")[0].scrollHeight),n&&n.css("visibility","visible")})})},onummatch:function(){this._super()}})})},"./client/src/bundles/Recipient.js":function(e,i,t){"use strict";var n=t(0);(function(e){return e&&e.__esModule?e:{default:e}})(n).default.entwine("ss",function(e){var i={updateFormatSpecificFields:function(){var i=e('input[name="SendPlain"]').is(":checked");e(".field.toggle-html-only")[i?"hide":"show"](),e(".field.toggle-plain-only")[i?"show":"hide"]()}};e("#Form_ItemEditForm .EmailRecipientForm").entwine({onmatch:function(){i.updateFormatSpecificFields()},onunmatch:function(){(void 0)._super()}}),e('#Form_ItemEditForm .EmailRecipientForm input[name="SendPlain"]').entwine({onchange:function(){i.updateFormatSpecificFields()}})})},"./client/src/bundles/bundle-cms.js":function(e,i,t){"use strict";t("./client/src/bundles/FieldEditor.js"),t("./client/src/bundles/Recipient.js")},0:function(e,i){e.exports=jQuery}}); \ No newline at end of file +!function(e){function t(o){if(r[o])return r[o].exports;var n=r[o]={i:o,l:!1,exports:{}};return e[o].call(n.exports,n,n.exports,t),n.l=!0,n.exports}var r={};t.m=e,t.c=r,t.i=function(e){return e},t.d=function(e,r,o){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle-cms.js")}({"./client/src/bundles/ConfirmFolder.js":function(e,t,r){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var n=Object.assign||function(e){for(var t=1;t');var r=e(this).closest("tr").data("id");t.data("id",r),e("body").append(t),t.open()}}}),e("#confirm-folder__dialog-wrapper").entwine({onunmatch:function(){this._clearModal()},open:function(){this._renderModal(!0)},close:function(t){if(!t){var r=e("#confirm-folder__dialog-wrapper").data("id");e(".ss-gridfield-item[data-id='"+r+"'] .dropdown.editable-column-field.form-group--no-label[data-folderconfirmed='0']").val("SilverStripe\\UserForms\\Model\\EditableFormField\\EditableTextField")}this._renderModal(!1)},_renderModal:function(t){var r=this,o=function(){return r._handleHideModal.apply(r,arguments)},s=function(){return r._handleSubmitModal.apply(r,arguments)},a=i.default._t("UserForms.FILE_CONFIRMATION_TITLE","Select file upload folder"),l=e(this).data("id"),h=e("#Form_EditForm_ID").attr("value"),c=m.default.parse("UserDefinedFormController/confirmfolderform"),f=F.default.parse(c.query);f.ID=l,f.UserFormID=h;var p=m.default.format(n({},c,{search:F.default.stringify(f)}));d.default.render(u.default.createElement(g,{title:a,isOpen:t,onSubmit:s,onClosed:o,schemaUrl:p,bodyClassName:"modal__dialog",className:"confirm-folder-modal",responseClassBad:"modal__response modal__response--error",responseClassGood:"modal__response modal__response--good",identifier:"UserForms.ConfirmFolder"}),this[0])},_clearModal:function(){d.default.unmountComponentAtNode(this[0])},_handleHideModal:function(){return this.close()},_handleSubmitModal:function(t,r,o){var n=this;return o().then(function(){l.default.noticeAdd({text:i.default._t("UserForms.FILE_CONFIRMATION_CONFIRMATION","Folder confirmed successfully."),stay:!1,type:"success"}),n.close(!0),e("#Form_EditForm_action_save").trigger("click")}).catch(function(e){l.default.noticeAdd({text:e.message,stay:!1,type:"error"})})}}),e("#Form_ConfirmFolderForm_action_cancel").entwine({onclick:function(){e("#confirm-folder__dialog-wrapper").close()}})})},"./client/src/bundles/FieldEditor.js":function(e,t,r){"use strict";var o=r(0);(function(e){return e&&e.__esModule?e:{default:e}})(o).default.entwine("ss",function(e){var t=null;e(".uf-field-editor .ss-gridfield-items").entwine({onmatch:function(){var r=0,o=0,n=e(".uf-field-editor .ss-gridfield-buttonrow").addClass("sticky-buttons"),s=e(".cms-content-header.north").first().height()+parseInt(e(".sticky-buttons").css("padding-top"),10),i=e(".uf-field-editor");this._super(),this.find(".ss-gridfield-item").each(function(t,n){switch(e(n).data("class")){case"SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFormStep":return void(o=0);case"SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFieldGroup":o+=1,r=o;break;case"SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFieldGroupEnd":r=o,o-=1;break;default:r=o}e(n).toggleClass("infieldgroup",r>0);for(var s=1;s<=5;s++)e(n).toggleClass("infieldgroup-level-"+s,r>=s)}),t=setInterval(function(){var e=i.offset().top;n.width("100%"),e>s||0===e?n.removeClass("sticky-buttons"):n.addClass("sticky-buttons")},300)},onunmatch:function(){this._super(),clearInterval(t)}}),e(".uf-field-editor .ss-gridfield-buttonrow .action").entwine({onclick:function(e){this._super(e),this.trigger("addnewinline")}}),e(".uf-field-editor").entwine({onmatch:function(){var t=this;this._super(),this.on("addnewinline",function(){t.one("reload",function(){var r=t.find(".ss-gridfield-item").last(),o=null;"SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFieldGroupEnd"===r.attr("data-class")?(o=r,o.prev().find(".col-Title input").focus(),r=o.add(o.prev()),o.css("visibility","hidden")):r.find(".col-Title input").focus(),r.addClass("flashBackground"),e(".cms-content-fields").scrollTop(e(".cms-content-fields")[0].scrollHeight),o&&o.css("visibility","visible")})})},onummatch:function(){this._super()}})})},"./client/src/bundles/Recipient.js":function(e,t,r){"use strict";var o=r(0);(function(e){return e&&e.__esModule?e:{default:e}})(o).default.entwine("ss",function(e){var t={updateFormatSpecificFields:function(){var t=e('input[name="SendPlain"]').is(":checked");e(".field.toggle-html-only")[t?"hide":"show"](),e(".field.toggle-plain-only")[t?"show":"hide"]()}};e("#Form_ItemEditForm .EmailRecipientForm").entwine({onmatch:function(){t.updateFormatSpecificFields()},onunmatch:function(){(void 0)._super()}}),e('#Form_ItemEditForm .EmailRecipientForm input[name="SendPlain"]').entwine({onchange:function(){t.updateFormatSpecificFields()}})})},"./client/src/bundles/bundle-cms.js":function(e,t,r){"use strict";r("./client/src/bundles/FieldEditor.js"),r("./client/src/bundles/ConfirmFolder.js"),r("./client/src/bundles/Recipient.js")},"./node_modules/punycode/punycode.js":function(e,t,r){(function(e,o){var n;!function(o){function s(e){throw new RangeError(S[e])}function i(e,t){for(var r=e.length,o=[];r--;)o[r]=t(e[r]);return o}function a(e,t){var r=e.split("@"),o="";return r.length>1&&(o=r[0]+"@",e=r[1]),e=e.replace(A,"."),o+i(e.split("."),t).join(".")}function l(e){for(var t,r,o=[],n=0,s=e.length;n=55296&&t<=56319&&n65535&&(e-=65536,t+=k(e>>>10&1023|55296),e=56320|1023&e),t+=k(e)}).join("")}function u(e){return e-48<10?e-22:e-65<26?e-65:e-97<26?e-97:_}function c(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function d(e,t,r){var o=0;for(e=r?U(e/w):e>>1,e+=U(e/t);e>q*y>>1;o+=_)e=U(e/q);return U(o+(q+1)*e/(e+j))}function f(e){var t,r,o,n,i,a,l,c,f,p,m=[],v=e.length,F=0,j=O,w=C;for(r=e.lastIndexOf(x),r<0&&(r=0),o=0;o=128&&s("not-basic"),m.push(e.charCodeAt(o));for(n=r>0?r+1:0;n=v&&s("invalid-input"),c=u(e.charCodeAt(n++)),(c>=_||c>U((g-F)/a))&&s("overflow"),F+=c*a,f=l<=w?b:l>=w+y?y:l-w,!(cU(g/p)&&s("overflow"),a*=p;t=m.length+1,w=d(F-i,t,0==i),U(F/t)>g-j&&s("overflow"),j+=U(F/t),F%=t,m.splice(F++,0,j)}return h(m)}function p(e){var t,r,o,n,i,a,h,u,f,p,m,v,F,j,w,I=[];for(e=l(e),v=e.length,t=O,r=0,i=C,a=0;a=t&&mU((g-r)/F)&&s("overflow"),r+=(h-t)*F,t=h,a=0;ag&&s("overflow"),m==t){for(u=r,f=_;p=f<=i?b:f>=i+y?y:f-i,!(u= 0x80 (not a basic code point)","invalid-input":"Invalid input"},q=_-b,U=Math.floor,k=String.fromCharCode;F={version:"1.4.1",ucs2:{decode:l,encode:h},decode:f,encode:p,toASCII:v,toUnicode:m},void 0!==(n=function(){return F}.call(t,r,t,e))&&(e.exports=n)}()}).call(t,r("./node_modules/webpack/buildin/module.js")(e),r("./node_modules/webpack/buildin/global.js"))},"./node_modules/querystring-es3/decode.js":function(e,t,r){"use strict";function o(e,t){return Object.prototype.hasOwnProperty.call(e,t)}e.exports=function(e,t,r,s){t=t||"&",r=r||"=";var i={};if("string"!=typeof e||0===e.length)return i;var a=/\+/g;e=e.split(t);var l=1e3;s&&"number"==typeof s.maxKeys&&(l=s.maxKeys);var h=e.length;l>0&&h>l&&(h=l);for(var u=0;u=0?(c=m.substr(0,v),d=m.substr(v+1)):(c=m,d=""),f=decodeURIComponent(c),p=decodeURIComponent(d),o(i,f)?n(i[f])?i[f].push(p):i[f]=[i[f],p]:i[f]=p}return i};var n=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},"./node_modules/querystring-es3/encode.js":function(e,t,r){"use strict";function o(e,t){if(e.map)return e.map(t);for(var r=[],o=0;o",'"',"`"," ","\r","\n","\t"],p=["{","}","|","\\","^","`"].concat(f),m=["'"].concat(p),v=["%","/","?",";","#"].concat(m),F=["/","?","#"],g=/^[+a-z0-9A-Z_-]{0,63}$/,_=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,b={javascript:!0,"javascript:":!0},y={javascript:!0,"javascript:":!0},j={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},w=r("./node_modules/querystring-es3/index.js");o.prototype.parse=function(e,t,r){if(!h.isString(e))throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),n=-1!==o&&o127?M+="x":M+=k[R];if(!M.match(g)){var H=q.slice(0,x),D=q.slice(x+1),T=k.match(_);T&&(H.push(T[1]),D.unshift(T[2])),D.length&&(a="/"+D.join(".")+a),this.hostname=H.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),S||(this.hostname=l.toASCII(this.hostname));var P=this.port?":"+this.port:"",L=this.hostname||"";this.host=L+P,this.href+=this.host,S&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==a[0]&&(a="/"+a))}if(!b[p])for(var x=0,U=m.length;x0)&&r.host.split("@");O&&(r.auth=O.shift(),r.host=r.hostname=O.shift())}return r.search=e.search,r.query=e.query,h.isNull(r.pathname)&&h.isNull(r.search)||(r.path=(r.pathname?r.pathname:"")+(r.search?r.search:"")),r.href=r.format(),r}if(!w.length)return r.pathname=null,r.search?r.path="/"+r.search:r.path=null,r.href=r.format(),r;for(var x=w.slice(-1)[0],I=(r.host||e.host||w.length>1)&&("."===x||".."===x)||""===x,E=0,A=w.length;A>=0;A--)x=w[A],"."===x?w.splice(A,1):".."===x?(w.splice(A,1),E++):E&&(w.splice(A,1),E--);if(!_&&!b)for(;E--;E)w.unshift("..");!_||""===w[0]||w[0]&&"/"===w[0].charAt(0)||w.unshift(""),I&&"/"!==w.join("/").substr(-1)&&w.push("");var S=""===w[0]||w[0]&&"/"===w[0].charAt(0);if(C){r.hostname=r.host=S?"":w.length?w.shift():"";var O=!!(r.host&&r.host.indexOf("@")>0)&&r.host.split("@");O&&(r.auth=O.shift(),r.host=r.hostname=O.shift())}return _=_||r.host&&w.length,_&&!S&&w.unshift(""),w.length?r.pathname=w.join("/"):(r.pathname=null,r.path=null),h.isNull(r.pathname)&&h.isNull(r.search)||(r.path=(r.pathname?r.pathname:"")+(r.search?r.search:"")),r.auth=e.auth||r.auth,r.slashes=r.slashes||e.slashes,r.href=r.format(),r},o.prototype.parseHost=function(){var e=this.host,t=c.exec(e);t&&(t=t[0],":"!==t&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)}},"./node_modules/url/util.js":function(e,t,r){"use strict";e.exports={isString:function(e){return"string"==typeof e},isObject:function(e){return"object"==typeof e&&null!==e},isNull:function(e){return null===e},isNullOrUndefined:function(e){return null==e}}},"./node_modules/webpack/buildin/global.js":function(e,t){var r;r=function(){return this}();try{r=r||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(r=window)}e.exports=r},"./node_modules/webpack/buildin/module.js":function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},0:function(e,t){e.exports=jQuery},1:function(e,t){e.exports=Injector},2:function(e,t){e.exports=React},3:function(e,t){e.exports=ReactDom},4:function(e,t){e.exports=i18n},5:function(e,t){e.exports=qs}}); \ No newline at end of file diff --git a/client/lang/en.js b/client/lang/en.js index f988818..a315b83 100644 --- a/client/lang/en.js +++ b/client/lang/en.js @@ -19,6 +19,8 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') { "UserForms.HIDE_OPTIONS": "Hide options", "UserForms.LEAVE_CONFIRMATION": "You have unsaved changes!", "UserForms.REMOVED_OPTION": "Removed option", - "UserForms.SHOW_OPTIONS": "Show options" + "UserForms.SHOW_OPTIONS": "Show options", + "UserForms.FILE_CONFIRMATION_TITLE": "Select file upload folder", + "UserForms.FILE_CONFIRMATION_CONFIRMATION": "Folder confirmed successfully." }); -} \ No newline at end of file +} diff --git a/client/lang/src/en.js b/client/lang/src/en.js index 3aa5ae1..e1578d5 100644 --- a/client/lang/src/en.js +++ b/client/lang/src/en.js @@ -12,5 +12,7 @@ "UserForms.HIDE_OPTIONS": "Hide options", "UserForms.LEAVE_CONFIRMATION": "You have unsaved changes!", "UserForms.REMOVED_OPTION": "Removed option", - "UserForms.SHOW_OPTIONS": "Show options" -} \ No newline at end of file + "UserForms.SHOW_OPTIONS": "Show options", + "UserForms.FILE_CONFIRMATION_TITLE": "Select file upload folder", + "UserForms.FILE_CONFIRMATION_CONFIRMATION": "Folder confirmed successfully." +} diff --git a/client/src/bundles/ConfirmFolder.js b/client/src/bundles/ConfirmFolder.js new file mode 100644 index 0000000..89ce518 --- /dev/null +++ b/client/src/bundles/ConfirmFolder.js @@ -0,0 +1,195 @@ +/* global window */ +import i18n from 'i18n'; +import jQuery from 'jquery'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { loadComponent } from 'lib/Injector'; +import url from 'url'; +import qs from 'qs'; + +const FormBuilderModal = loadComponent('FormBuilderModal'); + +jQuery.entwine('ss', ($) => { + /** Mark newly added fields as new */ + $('#Form_EditForm_Fields').entwine({ + onmatch() { + this._super(); + + this.on('addnewinline', () => { + this.one('reload', () => { + const newField = this.find('.ss-gridfield-item').last(); + newField.find('.col-ClassName select').attr('data-folderconfirmed', 0); + }); + }); + } + }); + + function toggleVisibility(check, show, hide) { + if (check) { + $(show).show(); + $(hide).hide(); + } else { + $(hide).show(); + $(show).hide(); + } + } + + /** Move our options under the radio button areas */ + $('#Form_ConfirmFolderForm_FolderOptions-new').entwine({ + onmatch() { + $('#Form_ConfirmFolderForm_CreateFolder_Holder').detach().appendTo($('#Form_ConfirmFolderForm_FolderOptions-new').parent().parent()); + toggleVisibility($(this).prop('checked'), '#Form_ConfirmFolderForm_CreateFolder_Holder', '#Form_ConfirmFolderForm_FolderID_Holder'); + }, + onchange() { + toggleVisibility($(this).prop('checked'), '#Form_ConfirmFolderForm_CreateFolder_Holder', '#Form_ConfirmFolderForm_FolderID_Holder'); + } + }); + + /** Move our options under the radio button areas */ + $('#Form_ConfirmFolderForm_FolderOptions-existing').entwine({ + onmatch() { + $('#Form_ConfirmFolderForm_FolderID_Holder').detach().appendTo($('#Form_ConfirmFolderForm_FolderOptions-existing').parent().parent()); + toggleVisibility($(this).prop('checked'), '#Form_ConfirmFolderForm_FolderID_Holder', '#Form_ConfirmFolderForm_CreateFolder_Holder'); + }, + onchange() { + toggleVisibility($(this).prop('checked'), '#Form_ConfirmFolderForm_FolderID_Holder', '#Form_ConfirmFolderForm_CreateFolder_Holder'); + } + }); + + /** Display permissions for folder selected */ + $('#Form_ConfirmFolderForm_FolderID_Holder .treedropdownfield.is-open,#Form_ItemEditForm_FolderID .treedropdownfield.is-open').entwine({ + onunmatch() { + // Build url + const parsedURL = url.parse('UserDefinedFormController/getfoldergrouppermissions'); + const parsedQs = qs.parse(parsedURL.query); + parsedQs.FolderID = $(this).find('input[name=FolderID]').val(); + const fetchURL = url.format({ ...parsedURL, search: qs.stringify(parsedQs) }); + + return fetch(fetchURL, { + credentials: 'same-origin', + }) + .then(response => response.json()) + .then(response => { + $(this).siblings('.form__field-description').html(response); + $(this).parent().siblings('.form__field-description').html(response); + return response; + }) + .catch((error) => { + jQuery.noticeAdd({ text: error.message, stay: false, type: 'error' }); + }); + } + }); + + /** + * Monitor new fields to intercept when EditableFileField is selected + */ + $(".uf-field-editor .ss-gridfield-items .dropdown.editable-column-field.form-group--no-label:not([data-folderconfirmed='1'])").entwine({ + onchange() { + // ensure EditableFileField is selected + if (this.get(0).value !== 'SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFileField') { + return; + } + + // ensure there are no other EditableFileField confirmed + if ($(".uf-field-editor .ss-gridfield-items .dropdown.editable-column-field.form-group--no-label[data-folderconfirmed='1']").length) { + return; + } + + // open folder confirmation dialog + let dialog = $('#confirm-folder__dialog-wrapper'); + + if (dialog.length) { + dialog.remove(); + } + + dialog = $('
'); + const id = $(this).closest('tr').data('id'); + dialog.data('id', id); + $('body').append(dialog); + + dialog.open(); + }, + }); + + /** handle modal rendering */ + $('#confirm-folder__dialog-wrapper').entwine({ + onunmatch() { + // solves errors given by ReactDOM "no matched root found" error. + this._clearModal(); + }, + + open() { + this._renderModal(true); + }, + + close(noRevert) { + if (!noRevert) { + // revert field to TextField + const id = $('#confirm-folder__dialog-wrapper').data('id'); + const select = $(`.ss-gridfield-item[data-id='${id}'] .dropdown.editable-column-field.form-group--no-label[data-folderconfirmed='0']`); + select.val('SilverStripe\\UserForms\\Model\\EditableFormField\\EditableTextField'); + } + + this._renderModal(false); + }, + + _renderModal(isOpen) { + const handleHide = (...args) => this._handleHideModal(...args); + const handleSubmit = (...args) => this._handleSubmitModal(...args); + const title = i18n._t('UserForms.FILE_CONFIRMATION_TITLE', 'Select file upload folder'); + const editableFileFieldID = $(this).data('id'); + const userFormID = $('#Form_EditForm_ID').attr('value'); + + // Build schema url + const parsedURL = url.parse('UserDefinedFormController/confirmfolderform'); + const parsedQs = qs.parse(parsedURL.query); + parsedQs.ID = editableFileFieldID; + parsedQs.UserFormID = userFormID; + const schemaUrl = url.format({ ...parsedURL, search: qs.stringify(parsedQs) }); + + ReactDOM.render( + , + this[0] + ); + }, + + _clearModal() { + ReactDOM.unmountComponentAtNode(this[0]); + // this.empty(); + }, + + _handleHideModal() { + // close the modal + return this.close(); + }, + + _handleSubmitModal(data, action, submitFn) { + return submitFn() + .then(() => { + jQuery.noticeAdd({ text: i18n._t('UserForms.FILE_CONFIRMATION_CONFIRMATION', 'Folder confirmed successfully.'), stay: false, type: 'success' }); + this.close(true); + $('#Form_EditForm_action_save').trigger('click'); + }) + .catch((error) => { + jQuery.noticeAdd({ text: error.message, stay: false, type: 'error' }); + }); + }, + }); + + $('#Form_ConfirmFolderForm_action_cancel').entwine({ + onclick() { + $('#confirm-folder__dialog-wrapper').close(); + } + }); +}); diff --git a/client/src/bundles/bundle-cms.js b/client/src/bundles/bundle-cms.js index 1ec80f6..04be3b0 100644 --- a/client/src/bundles/bundle-cms.js +++ b/client/src/bundles/bundle-cms.js @@ -1,3 +1,4 @@ // Used for CMS form fields import 'bundles/FieldEditor'; +import 'bundles/ConfirmFolder'; import 'bundles/Recipient'; diff --git a/code/Control/UserDefinedFormController.php b/code/Control/UserDefinedFormController.php index a14b21e..fabdcd6 100644 --- a/code/Control/UserDefinedFormController.php +++ b/code/Control/UserDefinedFormController.php @@ -2,33 +2,50 @@ namespace SilverStripe\UserForms\Control; -use Exception; use PageController; use Psr\Log\LoggerInterface; use SilverStripe\AssetAdmin\Controller\AssetAdmin; +use SilverStripe\Admin\LeftAndMain; use SilverStripe\Assets\File; +use SilverStripe\Assets\Folder; use SilverStripe\Assets\Upload; use SilverStripe\Control\Controller; use SilverStripe\Control\Email\Email; use SilverStripe\Control\HTTPResponse; use SilverStripe\Control\HTTPRequest; +use SilverStripe\Control\HTTPResponse_Exception; use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Manifest\ModuleLoader; +use SilverStripe\Forms\FieldList; use SilverStripe\Forms\Form; +use SilverStripe\Forms\FormAction; +use SilverStripe\Forms\HiddenField; +use SilverStripe\Forms\LiteralField; +use SilverStripe\Forms\OptionsetField; +use SilverStripe\Forms\Schema\FormSchema; +use SilverStripe\Forms\TextField; +use SilverStripe\Forms\TreeDropdownField; use SilverStripe\i18n\i18n; use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\ValidationException; use SilverStripe\ORM\ValidationResult; +use SilverStripe\Security\Group; +use SilverStripe\Security\InheritedPermissions; +use SilverStripe\Security\Permission; +use SilverStripe\Security\PermissionFailureException; use SilverStripe\Security\Security; use SilverStripe\UserForms\Extension\UserFormFileExtension; use SilverStripe\UserForms\Form\UserForm; use SilverStripe\UserForms\Model\EditableFormField; use SilverStripe\UserForms\Model\EditableFormField\EditableFileField; use SilverStripe\UserForms\Model\Submission\SubmittedForm; +use SilverStripe\UserForms\Model\UserDefinedForm; +use SilverStripe\Versioned\Versioned; use SilverStripe\View\ArrayData; use SilverStripe\View\Requirements; use SilverStripe\View\SSViewer; +use SilverStripe\View\ViewableData; use Swift_RfcComplianceException; /** @@ -44,9 +61,15 @@ class UserDefinedFormController extends PageController 'index', 'ping', 'Form', - 'finished' + 'finished', + 'confirmfolderform' => 'CMS_ACCESS_CMSMain', + 'confirmfolder' => 'CMS_ACCESS_CMSMain', + 'getfoldergrouppermissions' => 'CMS_ACCESS_CMSMain', ]; + /** @var string The name of the folder where form submissions will be placed by default */ + private static $form_submissions_folder = 'Form-submissions'; + protected function init() { parent::init(); @@ -522,6 +545,240 @@ JS ]); } + /** + * Returns a TextField for entering a folder name. + * @param string $folder The current folder to set the field to + * @param string $title The title of the text field + * @return TextField + */ + private static function getRestrictedAccessField(string $folder, string $title) + { + /** @var TextField $textField */ + $textField = TextField::create('CreateFolder', ''); + + /** @var Folder $formSubmissionsFolder */ + $formSubmissionsFolder = Folder::find($folder); + $textField->setDescription(EditableFileField::getFolderPermissionString($formSubmissionsFolder)); + $textField->addExtraClass('pt-2'); + $textField->setSchemaData([ + 'data' => [ + 'prefix' => static::config()->get('form_submissions_folder') . '/', + ], + 'attributes' => [ + 'placeholder' => $title + ] + ]); + + return $textField; + } + + /** + * This returns a Confirm Folder form used to verify the upload folder for EditableFileFields + * + * @return ViewableData + */ + public function confirmfolderform() + { + $request = $this->getRequest(); + $id = $request->requestVar('ID'); + if (!$id) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + $userFormID = $request->requestVar('UserFormID'); + if (!$userFormID) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + $userForm = UserDefinedForm::get()->byID($userFormID); + if (!$userForm) { + $userForm = Versioned::get_by_stage(UserDefinedForm::class, Versioned::DRAFT)->byID($userFormID); + } + if (!$userForm) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + + if (!$userForm->canEdit()) { + throw new PermissionFailureException(); + } + + $editableFormField = EditableFormField::get()->filter(['ID' => $id, 'ParentID' => $userFormID])->first(); + if (!$editableFormField) { + $editableFormField = Versioned::get_by_stage(EditableFormField::class, Versioned::DRAFT) + ->filter(['ID' => $id, 'ParentID' => $userFormID])->first(); + } + + $folderId = 0; + if (!$editableFormField) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + + if ($editableFormField instanceof EditableFileField) { + $folderId = $editableFormField->FolderID; + } + /** @var Folder $folder */ + $folder = Folder::get()->byID($folderId); + if (!$folder) { + $folder = $this->getFormSubmissionFolder(); + } + + $fields = FieldList::create(); + + $labelA = LiteralField::create('LabelA', _t(__CLASS__.'.CONFIRM_FOLDER_LABEL_A', 'Files that your users upload should be stored carefully to reduce the risk of exposing sensitive data. Ensure the folder you select can only be viewed by appropriate parties. Folder permissions can be managed within the Files area.')); + $labelA->addExtraClass(' mb-2'); + $fields->push($labelA); + + $labelB = LiteralField::create('LabelB', _t(__CLASS__.'.CONFIRM_FOLDER_LABEL_B', 'The folder selected will become the default for this form. This can be changed on an individual basis in the File upload field.')); + $labelB->addExtraClass(' mb-3'); + $fields->push($labelB); + + $fields->push(static::getRestrictedAccessField($this->config()->get('form_submissions_folder'), $userForm->Title)); + + $options = OptionsetField::create('FolderOptions', _t(__CLASS__.'.FOLDER_OPTIONS_TITLE', 'Form folder options'), [ + "new" => _t(__CLASS__.'.FOLDER_OPTIONS_NEW', 'Create a new folder (recommended)'), + "existing" => _t(__CLASS__.'.FOLDER_OPTIONS_EXISTING', 'Use an existing folder') + ], "new"); + $fields->push($options); + + + $treeView = TreeDropdownField::create( + 'FolderID', + '', + Folder::class + )->setValue($folder->ID); + $treeView->addExtraClass('pt-1'); + $treeView->setDescription(EditableFileField::getFolderPermissionString($folder)); + $fields->push($treeView); + + + $fields->push(HiddenField::create('ID', 'ID', $editableFormField->ID)); + + $submitAction = FormAction::create('confirmfolder', _t(__CLASS__.'.FORM_ACTION_CONFIRM', 'Save and continue')); + $submitAction->setUseButtonTag(false); + $submitAction->addExtraClass('btn'); + $submitAction->addExtraClass('btn-primary'); + + $cancelAction = FormAction::create("cancel", _t('SilverStripe\\CMS\\Controllers\\CMSMain.Cancel', "Cancel")) + ->addExtraClass('btn-secondary') + ->setUseButtonTag(true); + + $form = Form::create($this, 'ConfirmFolderForm', $fields, FieldList::create($submitAction, $cancelAction)); + $form->setFormAction("UserDefinedFormController/confirmfolder"); + $form->addExtraClass('form--no-dividers'); + + + if (!$editableFormField instanceof EditableFileField) { + $editableFormField = $editableFormField->newClassInstance(EditableFileField::class); + $editableFormField->write(); + } + $treeView->setSchemaData([ + 'data' => [ + 'urlTree' => "admin/pages/edit/EditForm/$userFormID/field/Fields/item/$id/ItemEditForm/field/FolderID/tree" + ] + ]); + + + // create the schema response + $parts = $this->getRequest()->getHeader(LeftAndMain::SCHEMA_HEADER); + $schemaID = $this->getRequest()->getURL(); + $data = FormSchema::singleton() + ->getMultipartSchema($parts, $schemaID, $form); + + // return the schema response + $response = HTTPResponse::create(json_encode($data)); + $response->addHeader('Content-Type', 'application/json'); + return $response; + } + + /** + * Sets the selected folder as the upload folder for an EditableFileField + * @return HTTPResponse + * @throws ValidationException + */ + public function confirmfolder() + { + if (!Permission::checkMember(null, "CMS_ACCESS_AssetAdmin")) { + throw new PermissionFailureException(); + } + + $request = $this->getRequest(); + + // retrieve the EditableFileField + $id = $request->requestVar('ID'); + if (!$id) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + /** @var EditableFileField $editableFileField */ + $editableFormField = EditableFormField::get()->byID($id); + if (!$editableFormField) { + $editableFormField = Versioned::get_by_stage(EditableFormField::class, Versioned::DRAFT)->byID($id); + } + if (!$editableFormField) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + // change the class if it is incorrect + if (!$editableFormField instanceof EditableFileField) { + $editableFormField = $editableFormField->newClassInstance(EditableFileField::class); + } + if (!$editableFormField) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + $editableFileField = $editableFormField; + + if (!$editableFileField->canEdit()) { + throw new PermissionFailureException(); + } + + // check if we're creating a new folder or using an existing folder + $option = $request->requestVar('FolderOptions'); + if ($option === 'existing') { + // set existing folder + $folderID = $request->requestVar('FolderID'); + if ($folderID != 0) { + $folder = Folder::get()->byID($folderID); + if (!$folder) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + } + } else { + // create the folder + $createFolder = $request->requestVar('CreateFolder') ?: $editableFormField->Parent()->Title; + $folder = $this->getFormSubmissionFolder($createFolder); + } + + // assign the folder + $editableFileField->FolderID = isset($folder) ? $folder->ID : 0; + $editableFileField->write(); + + // respond + $response = HTTPResponse::create(json_encode([])); + $response->addHeader('Content-Type', 'application/json'); + return $response; + } + + /** + * @return HTTPResponse + */ + public function getfoldergrouppermissions() + { + $folderID = $this->getRequest()->requestVar('FolderID'); + if ($folderID) { + /** @var Folder $folder */ + $folder = Folder::get()->byID($folderID); + if (!$folder) { + throw new HTTPResponse_Exception(_t(__CLASS__.'.INVALID_REQUEST', 'This request was invalid.'), 400); + } + if (!$folder->canView()) { + throw new PermissionFailureException(); + } + } else { + $folder = null; + } + + // respond + $response = HTTPResponse::create(json_encode(EditableFileField::getFolderPermissionString($folder))); + $response->addHeader('Content-Type', 'application/json'); + return $response; + } + /** * Outputs the required JS from the $watch input * @@ -559,4 +816,44 @@ EOS; return $result; } + + /** + * @throws ValidationException + */ + private static function updateFormSubmissionFolderPermissions() + { + // ensure the FormSubmissions folder is only accessible to Administrators + $formSubmissionsFolder = Folder::find(self::config()->get('form_submissions_folder')); + $formSubmissionsFolder->CanViewType = InheritedPermissions::ONLY_THESE_USERS; + $formSubmissionsFolder->ViewerGroups()->removeAll(); + $formSubmissionsFolder->ViewerGroups()->add(Group::get_one(Group::class, ['"Code"' => 'administrators'])); + $formSubmissionsFolder->write(); + } + + /** + * Returns the form submission folder or a sub folder if provided. + * Creates the form submission folder if it doesn't exist. + * Updates the form submission folder permissions if it is created. + * @param string $subFolder Sub-folder to be created or returned. + * @return Folder + * @throws ValidationException + */ + public static function getFormSubmissionFolder(string $subFolder = null): ?Folder + { + $folderPath = self::config()->get('form_submissions_folder'); + if ($subFolder) { + $folderPath .= '/' . $subFolder; + } + $formSubmissionsFolderExists = !!Folder::find(self::config()->get('form_submissions_folder')); + $folder = Folder::find_or_make($folderPath); + + // Set default permissions if this is the first time we create the form submission folder + if (!$formSubmissionsFolderExists) { + self::updateFormSubmissionFolderPermissions(); + // Make sure we return the folder with the latest permission + $folder = Folder::find($folderPath); + } + + return $folder; + } } diff --git a/code/Extension/UserFormFieldEditorExtension.php b/code/Extension/UserFormFieldEditorExtension.php index 8780c6e..ac2e246 100644 --- a/code/Extension/UserFormFieldEditorExtension.php +++ b/code/Extension/UserFormFieldEditorExtension.php @@ -17,6 +17,7 @@ use SilverStripe\UserForms\Form\GridFieldAddClassesButton; use SilverStripe\UserForms\Model\EditableFormField; use SilverStripe\UserForms\Model\EditableFormField\EditableFieldGroup; use SilverStripe\UserForms\Model\EditableFormField\EditableFieldGroupEnd; +use SilverStripe\UserForms\Model\EditableFormField\EditableFileField; use SilverStripe\UserForms\Model\EditableFormField\EditableFormStep; use SilverStripe\UserForms\Model\EditableFormField\EditableTextField; use SilverStripe\Versioned\Versioned; @@ -66,6 +67,8 @@ class UserFormFieldEditorExtension extends DataExtension */ public function getFieldEditorGrid() { + Requirements::javascript('silverstripe/admin:client/dist/js/vendor.js'); + Requirements::javascript('silverstripe/admin:client/dist/js/bundle.js'); Requirements::javascript('silverstripe/userforms:client/dist/js/userforms-cms.js'); $fields = $this->owner->Fields(); @@ -77,7 +80,11 @@ class UserFormFieldEditorExtension extends DataExtension $editableColumns->setDisplayFields([ 'ClassName' => function ($record, $column, $grid) use ($fieldClasses) { if ($record instanceof EditableFormField) { - return $record->getInlineClassnameField($column, $fieldClasses); + $field = $record->getInlineClassnameField($column, $fieldClasses); + if ($record instanceof EditableFileField) { + $field->setAttribute('data-folderconfirmed', $record->FolderConfirmed ? 1 : 0); + } + return $field; } }, 'Title' => function ($record, $column, $grid) { diff --git a/code/Model/EditableFormField/EditableFileField.php b/code/Model/EditableFormField/EditableFileField.php index 67f6627..9620e31 100755 --- a/code/Model/EditableFormField/EditableFileField.php +++ b/code/Model/EditableFormField/EditableFileField.php @@ -13,6 +13,8 @@ use SilverStripe\Forms\NumericField; use SilverStripe\Forms\TreeDropdownField; use SilverStripe\ORM\ValidationResult; use SilverStripe\Security\Member; +use SilverStripe\Security\InheritedPermissions; +use SilverStripe\UserForms\Control\UserDefinedFormController; use SilverStripe\UserForms\Model\EditableFormField; use SilverStripe\UserForms\Model\Submission\SubmittedFileField; @@ -21,7 +23,8 @@ use SilverStripe\UserForms\Model\Submission\SubmittedFileField; * * @method Folder Folder * @property int FolderID - * @property float MaxFileSizeMB + * @property boolean MaxFileSizeMB + * @property boolean FolderConfirmed * @package userforms */ class EditableFileField extends EditableFormField @@ -33,6 +36,7 @@ class EditableFileField extends EditableFormField private static $db = [ 'MaxFileSizeMB' => 'Float', + 'FolderConfirmed' => 'Boolean', ]; private static $has_one = [ @@ -50,6 +54,85 @@ class EditableFileField extends EditableFormField 'htm', 'html', 'xhtml', 'swf', 'xml' ]; + /** + * Returns a string describing the permissions of a folder + * @param Folder|null $folder + * @return string + */ + public static function getFolderPermissionString(Folder $folder = null) + { + $folderPermissions = static::getFolderPermissionTuple($folder); + + $icon = 'font-icon-user-lock'; + if ($folderPermissions['CanViewType'] == InheritedPermissions::ANYONE) { + $icon = 'font-icon-address-card-warning'; + } + + return sprintf( + '%s %s', + $icon, + $folderPermissions['CanViewTypeString'], + htmlspecialchars(implode(', ', $folderPermissions['ViewerGroups']), ENT_QUOTES) + ); + } + + /** + * Returns an array with a view type string and the viewer groups + * @param Folder|null $folder + * @return array + */ + private static function getFolderPermissionTuple(Folder $folder = null) + { + $viewersOptionsField = [ + InheritedPermissions::INHERIT => _t(__CLASS__.'.INHERIT', 'Visibility for this folder is inherited from the parent folder'), + InheritedPermissions::ANYONE => _t(__CLASS__.'.ANYONE', 'Unrestricted access, uploads will be visible to anyone'), + InheritedPermissions::LOGGED_IN_USERS => _t(__CLASS__.'.LOGGED_IN', 'Restricted access, uploads will be visible to logged-in users'), + InheritedPermissions::ONLY_THESE_USERS => _t(__CLASS__.'.ONLY_GROUPS', 'Restricted access, uploads will be visible to the following groups:') + ]; + + if (!$folder) { + return [ + 'CanViewType' => InheritedPermissions::ANYONE, + 'CanViewTypeString' => $viewersOptionsField[InheritedPermissions::ANYONE], + 'ViewerGroups' => [], + ]; + } + + $folder = static::getNonInheritedViewType($folder); + + // ViewerGroups may still exist when permissions have been loosened + $viewerGroups = []; + if ($folder->CanViewType === InheritedPermissions::ONLY_THESE_USERS) { + $viewerGroups = $folder->ViewerGroups()->column('Title'); + } + + return [ + 'CanViewType' => $folder->CanViewType, + 'CanViewTypeString' => $viewersOptionsField[$folder->CanViewType], + 'ViewerGroups' => $viewerGroups, + ]; + } + + /** + * Returns the nearest non-inherited view permission of the provided + * @param File $file + * @return File + */ + private static function getNonInheritedViewType(File $file) + { + if ($file->CanViewType !== InheritedPermissions::INHERIT) { + return $file; + } + $parent = $file->Parent(); + if ($parent->exists()) { + return static::getNonInheritedViewType($parent); + } else { + // anyone can view top level files + $file->CanViewType = InheritedPermissions::ANYONE; + } + return $file; + } + /** * @return FieldList */ @@ -57,13 +140,15 @@ class EditableFileField extends EditableFormField { $fields = parent::getCMSFields(); + $treeView = TreeDropdownField::create( + 'FolderID', + _t('EditableUploadField.SELECTUPLOADFOLDER', 'Select upload folder'), + Folder::class + ); + $treeView->setDescription(static::getFolderPermissionString($this->Folder())); $fields->addFieldToTab( 'Root.Main', - TreeDropdownField::create( - 'FolderID', - _t('EditableUploadField.SELECTUPLOADFOLDER', 'Select upload folder'), - Folder::class - ) + $treeView ); // Warn the user if the folder targeted by this field is not restricted @@ -172,4 +257,30 @@ class EditableFileField extends EditableFormField { return round(static::get_php_max_file_size() / 1024 / 1024, 1); } + + public function onBeforeWrite() + { + parent::onBeforeWrite(); + + $folderChanged = $this->isChanged('FolderID'); + + // Default to either an existing sibling's folder, or the default form submissions folder + if ($this->FolderID === null) { + $inheritableSibling = EditableFileField::get()->filter([ + 'ParentID' => $this->ParentID, + 'FolderConfirmed' => true, + ])->first(); + + if ($inheritableSibling) { + $this->FolderID = $inheritableSibling->FolderID; + } else { + $folder = UserDefinedFormController::getFormSubmissionFolder(); + $this->FolderID = $folder->ID; + } + } + + if ($folderChanged) { + $this->FolderConfirmed = true; + } + } } diff --git a/lang/en.yml b/lang/en.yml index 29ef343..36f8e9b 100644 --- a/lang/en.yml +++ b/lang/en.yml @@ -287,3 +287,15 @@ en: TYPEREPLY: 'Type reply address' TYPESUBJECT: 'Type subject' TYPETO: 'Type to address' + SilverStripe\UserForms\Control\UserDefinedFormController: + INVALID_REQUEST: 'This request was invalid.' + CONFIRM_FOLDER_LABEL_A: 'Files that your users upload should be stored carefully to reduce the risk of exposing sensitive data. Ensure the folder you select can only be viewed by appropriate parties. Folder permissions can be managed within the Files area.' + CONFIRM_FOLDER_LABEL_B: 'The folder selected will become the default for this form. This can be changed on an individual basis in the File upload field.' + FOLDER_OPTIONS_TITLE: 'Form folder options' + FOLDER_OPTIONS_NEW: 'Create a new folder (recommended)' + FOLDER_OPTIONS_EXISTING: 'Use an existing folder' + FORM_ACTION_CONFIRM: 'Save and continue' + FOLDER_PERMISSIONS_INHERIT: 'Visibility for this folder is inherited from the parent folder' + FOLDER_PERMISSIONS_ANYONE: 'Unrestricted access, uploads will be visible to anyone' + FOLDER_PERMISSIONS_LOGGED_IN: 'Restricted access, uploads will be visible to logged-in users' + FOLDER_PERMISSIONS_ONLY_GROUPS: 'Restricted access, uploads will be visible to the following groups:' diff --git a/package.json b/package.json index 227ac76..f1f11d3 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,10 @@ "dependencies": { "babel-preset-es2016": "^6.24.1", "jquery": "^3.5.0", - "mime": "^1.4.1" + "mime": "^1.4.1", + "qs": "^6.9.4", + "react": "^16.13.1", + "react-dom": "^16.13.1" }, "babel": { "presets": [ diff --git a/tests/Control/UserDefinedFormControllerTest.php b/tests/Control/UserDefinedFormControllerTest.php index 709f9d4..2a8e478 100644 --- a/tests/Control/UserDefinedFormControllerTest.php +++ b/tests/Control/UserDefinedFormControllerTest.php @@ -4,8 +4,11 @@ namespace SilverStripe\UserForms\Tests\Control; use SilverStripe\Assets\Dev\TestAssetStore; use SilverStripe\Assets\File; +use SilverStripe\Assets\Folder; use SilverStripe\Assets\Storage\AssetStore; use SilverStripe\Assets\Upload_Validator; +use InvalidArgumentException; +use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPResponse; use SilverStripe\Control\Session; use SilverStripe\Core\Config\Config; @@ -15,7 +18,9 @@ use SilverStripe\Dev\FunctionalTest; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FormAction; use SilverStripe\ORM\DataObject; +use SilverStripe\Security\InheritedPermissions; use SilverStripe\UserForms\Control\UserDefinedFormController; +use SilverStripe\UserForms\Model\EditableFormField; use SilverStripe\UserForms\Model\EditableFormField\EditableFileField; use SilverStripe\UserForms\Model\EditableFormField\EditableTextField; use SilverStripe\UserForms\Model\Recipient\EmailRecipient; @@ -43,6 +48,14 @@ class UserDefinedFormControllerTest extends FunctionalTest TestAssetStore::activate('AssetStoreTest'); Config::modify()->merge(SSViewer::class, 'themes', ['simple', '$default']); + $submissionFolder = Folder::find('Form-submissions'); + if ($submissionFolder) { + $submissionFolder->delete(); + } + + foreach (Folder::get() as $folder) { + $folder->publishSingle(); + } } public function tearDown() @@ -384,6 +397,437 @@ class UserDefinedFormControllerTest extends FunctionalTest $this->assertEmailSent('test@example.com', 'no-reply@example.com', 'Email Subject: Basic Value'); } + public function testConfirmfolderformInvalidRequest() + { + $this->logInWithPermission('CMS_ACCESS_CMSMain'); + + $url = 'UserDefinedFormController/confirmfolderform?'; + $userDefinedFormID = $this->idFromFixture(UserDefinedForm::class, 'basic-form-page'); + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + + $response = $this->get($url . http_build_query(['UserFormID' => $userDefinedFormID])); + $this->assertEquals(400, $response->getStatusCode(), 'Request without ID parameter is invalid'); + + $response = $this->get($url . http_build_query(['ID' => $fieldID])); + $this->assertEquals(400, $response->getStatusCode(), 'Request without UserFormID parameter is invalid'); + + $response = $this->get($url . http_build_query(['ID' => $fieldID, 'UserFormID' => -1])); + $this->assertEquals(400, $response->getStatusCode(), 'Request with unknown UserFormID is invalid'); + + $response = $this->get($url . http_build_query(['ID' => -1, 'UserFormID' => $userDefinedFormID])); + $this->assertEquals(400, $response->getStatusCode(), 'Request with unknown ID and known UserFormID is invalid'); + } + + public function testConfirmfolderformAccessControl() + { + $url = 'UserDefinedFormController/confirmfolderform?'; + $userDefinedFormID = $this->idFromFixture(UserDefinedForm::class, 'upload-form'); + $restrictedUserDefinedFormID = $this->idFromFixture(UserDefinedForm::class, 'restricted-user-form'); + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + $restrictedFieldID = $this->idFromFixture(EditableFileField::class, 'file-field-2'); + + $this->logOut(); + $response = $this->get($url . http_build_query(['ID' => $fieldID, 'UserFormID' => $userDefinedFormID])); + $this->assertEquals(403, $response->getStatusCode(), 'Anonymous users can\'t access confirm folder form '); + + $this->logInWithPermission('CMS_ACCESS_CMSMain'); + + $response = $this->get($url . http_build_query(['ID' => $fieldID, 'UserFormID' => $userDefinedFormID])); + $this->assertEquals(200, $response->getStatusCode(), 'CMS editors can access confirm folder form '); + + $response = $this->get($url . http_build_query([ + 'ID' => $restrictedFieldID, + 'UserFormID' => $restrictedUserDefinedFormID + ])); + $this->assertEquals( + 403, + $response->getStatusCode(), + 'CMS editors can\'t access confirm folder form for restricted form' + ); + + $this->logInWithPermission('ADMIN'); + + $response = $this->get($url . http_build_query([ + 'ID' => $restrictedFieldID, + 'UserFormID' => $restrictedUserDefinedFormID + ])); + $this->assertEquals( + 200, + $response->getStatusCode(), + 'Admins can access confirm folder form for restricted form' + ); + } + + public function testConfirmfolderformFields() + { + $url = 'UserDefinedFormController/confirmfolderform?'; + $userDefinedFormID = $this->idFromFixture(UserDefinedForm::class, 'upload-form'); + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + $folderID = $this->idFromFixture(Folder::class, 'unrestricted'); + $this->logInWithPermission('ADMIN'); + + $response = $this->get( + $url . http_build_query(['ID' => $fieldID, 'UserFormID' => $userDefinedFormID]), + null, + ['X-FormSchema-Request' => 'auto,schema,state,errors'] + ); + $schemaData = json_decode($response->getBody(), true); + + $this->assertEquals('ConfirmFolderForm', $schemaData['schema']['name']); + $this->assertField($schemaData, 'FolderOptions', ['component' => 'OptionsetField']); + $this->assertField($schemaData, 'FolderID', ['component' => 'TreeDropdownField']); + $this->assertField($schemaData, 'ID', ['schemaType' =>'Hidden']); + + $this->assertStateValue($schemaData, ['ID' => $fieldID, 'FolderID' => $folderID]); + } + + public function testConfirmfolderformDefaultFolder() + { + $url = 'UserDefinedFormController/confirmfolderform?'; + $userDefinedFormID = $this->idFromFixture(UserDefinedForm::class, 'restricted-user-form'); + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-2'); + + $this->logInWithPermission('ADMIN'); + + $response = $this->get( + $url . http_build_query(['ID' => $fieldID, 'UserFormID' => $userDefinedFormID]), + null, + ['X-FormSchema-Request' => 'auto,schema,state,errors'] + ); + $schemaData = json_decode($response->getBody(), true); + + $this->assertEquals('ConfirmFolderForm', $schemaData['schema']['name']); + $this->assertField($schemaData, 'FolderOptions', ['component' => 'OptionsetField']); + $this->assertField($schemaData, 'FolderID', ['component' => 'TreeDropdownField']); + $this->assertField($schemaData, 'ID', ['schemaType' =>'Hidden']); + + $folder = Folder::find('Form-submissions'); + $this->assertNotEmpty($folder, 'Default submission folder has been created'); + + $this->assertStateValue($schemaData, ['ID' => $fieldID, 'FolderID' => $folder->ID]); + + $this->logOut(); + $this->assertFalse($folder->canView(), 'Default submission folder is protected'); + } + + public function testConfirmfolderInvalidRequest() + { + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + + $url = 'UserDefinedFormController/confirmfolder?'; + $response = $this->post($url, []); + $this->assertEquals(400, $response->getStatusCode(), 'Request without ID parameter is invalid'); + + $response = $this->post($url, ['ID' => -1]); + $this->assertEquals(400, $response->getStatusCode(), 'Request without ID parameter is invalid'); + } + + public function testConfirmfolderAccessControl() + { + $url = 'UserDefinedFormController/confirmfolder?'; + $userDefinedFormID = $this->idFromFixture(UserDefinedForm::class, 'upload-form'); + $restrictedUserDefinedFormID = $this->idFromFixture(UserDefinedForm::class, 'restricted-user-form'); + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + $restrictedFieldID = $this->idFromFixture(EditableFileField::class, 'file-field-2'); + + $this->logOut(); + $response = $this->post($url, ['ID' => $fieldID]); + $this->assertEquals(403, $response->getStatusCode(), 'Anonymous users can\'t confirm folder '); + + $this->logInWithPermission('CMS_ACCESS_CMSMain'); + $response = $this->post($url, ['ID' => $fieldID]); + $this->assertEquals( + 403, + $response->getStatusCode(), + 'Users without CMS_ACCESS_AssetAdmin can\'t confirm folder' + ); + + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + $response = $this->post($url, ['ID' => $fieldID]); + $this->assertEquals(200, $response->getStatusCode(), 'CMS editors can access confirm folder form '); + + $response = $this->post($url, ['ID' => $restrictedFieldID]); + $this->assertEquals( + 403, + $response->getStatusCode(), + 'CMS editors can\'t confirm folder form for restricted form' + ); + + $this->logInWithPermission('ADMIN'); + + $response = $this->post($url, ['ID' => $restrictedFieldID]); + $this->assertEquals( + 200, + $response->getStatusCode(), + 'Admins can confirm folder form for restricted form' + ); + } + + public function testConfirmfolderExistingFolder() + { + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + + $url = 'UserDefinedFormController/confirmfolder?'; + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + $folderID = $this->idFromFixture(Folder::class, 'restricted'); + + $response = $this->post($url, ['ID' => $fieldID, 'FolderOptions' => 'existing', 'FolderID' => $folderID]); + $this->assertEquals(200, $response->getStatusCode(), 'Valid request to confirm an existing folder is successful'); + $this->assertEquals( + $folderID, + EditableFileField::get()->byID($fieldID)->FolderID, + 'FileField points to restricted folder' + ); + } + + public function testConfirmfolderInexistingFolder() + { + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + + $url = 'UserDefinedFormController/confirmfolder?'; + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + + $response = $this->post($url, ['ID' => $fieldID, 'FolderOptions' => 'existing', 'FolderID' => -1]); + $this->assertEquals(400, $response->getStatusCode(), 'Confirm a non-existant folder fails with 400'); + } + + public function testConfirmfolderRootFolder() + { + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + + $url = 'UserDefinedFormController/confirmfolder?'; + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + + $response = $this->post($url, ['ID' => $fieldID, 'FolderOptions' => 'existing', 'FolderID' => 0]); + $this->assertEquals(200, $response->getStatusCode(), 'Valid request to confirm an root folder is successful'); + $this->assertEquals(0, EditableFileField::get()->byID($fieldID)->FolderID, 'FileField points to root folder'); + } + + public function testConfirmfolderNewFolder() + { + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + + $url = 'UserDefinedFormController/confirmfolder?'; + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + + $response = $this->post($url, ['ID' => $fieldID, 'FolderOptions' => 'new']); + $this->assertEquals(200, $response->getStatusCode(), 'Valid request to confirm folder by creating a new one is valid'); + + $folder = Folder::find('Form-submissions/Form-with-upload-field'); + $this->assertNotEmpty($folder, 'New folder has been created based on the UserFormPage\'s title'); + + $this->logOut(); + $this->assertFalse($folder->canView(), 'New folder is restricted'); + } + + public function testConfirmfolderNewFolderWithSpecificName() + { + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + + $url = 'UserDefinedFormController/confirmfolder?'; + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + + $response = $this->post( + $url, + ['ID' => $fieldID, 'FolderOptions' => 'new', 'CreateFolder' => 'My-Custom-Folder->\'Pow'] + ); + $this->assertEquals(200, $response->getStatusCode(), 'Valid request to confirm folder by creating a new one is valid'); + + $folder = Folder::find('Form-submissions/My-Custom-Folder-Pow'); + $this->assertNotEmpty($folder, 'New folder has been created based the provided CreateFolder value'); + + $this->logOut(); + $this->assertFalse($folder->canView(), 'New folder is restricted'); + } + + public function testConfirmfolderWithFieldTypeConversion() + { + $this->logInWithPermission('ADMIN'); + + $url = 'UserDefinedFormController/confirmfolder?'; + $fieldID = $this->idFromFixture(EditableTextField::class, 'become-file-upload'); + + $response = $this->post($url, ['ID' => $fieldID, 'FolderOptions' => 'new']); + $this->assertEquals(200, $response->getStatusCode(), 'Valid request to confirm folder by creating a new one is valid'); + + $folder = Folder::find('Form-submissions/Form-editable-only-by-admin'); + $this->assertNotEmpty($folder, 'New folder has been created based on the UserFormPage\'s title'); + + $this->logOut(); + $this->assertFalse($folder->canView(), 'New folder is restricted'); + + $field = EditableFormField::get()->byID($fieldID); + $this->assertEquals( + EditableFileField::class, + $field->ClassName, + 'EditableTextField has been converted to EditableFileField' + ); + } + + public function testPreserveSubmissionFolderPermission() + { + $folder = Folder::find_or_make('Form-submissions'); + $folder->CanViewType = InheritedPermissions::ANYONE; + $folder->write(); + + + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + $url = 'UserDefinedFormController/confirmfolder?'; + $fieldID = $this->idFromFixture(EditableFileField::class, 'file-field-1'); + + $this->post($url, ['ID' => $fieldID, 'FolderOptions' => 'new']); + + $folder = Folder::find('Form-submissions'); + + $this->assertEquals( + InheritedPermissions::ANYONE, + $folder->CanViewType, + 'Submission folder permissions are preserved' + ); + } + + /** + * Assert that a field with the provided attribute exists in $schema. + * + * @param array $schema + * @param string $name + * @param string $component + * @param $value + * @param string $message + */ + private function assertField(array $schema, string $name, array $attributes, $message = '') + { + $message = $message ?: sprintf('A %s field exists with %s', $name, var_export($attributes, true)); + $fields = $schema['schema']['fields']; + $state = $schema['state']['fields']; + $this->assertNotEmpty($fields, $message); + $foundField = false; + foreach ($fields as $field) { + if ($field['name'] === $name) { + $foundField = true; + foreach ($attributes as $attr => $expectedValue) { + $this->assertEquals($expectedValue, $field[$attr]); + } + break; + } + } + $this->assertTrue($foundField, $message); + } + + private function assertStateValue(array $schema, $values) + { + $fields = $schema['state']['fields']; + $this->assertNotEmpty($fields); + $foundField = false; + foreach ($fields as $field) { + $key = $field['name']; + if (isset($values[$key])) { + $this->assertEquals($values[$key], $field['value'], sprintf('%s is %s', $key, $values[$key])); + } + } + } + + public function testGetFolderPermissionAccessControl() + { + $this->logOut(); + $url = 'UserDefinedFormController/getfoldergrouppermissions?'; + $folder = Folder::find('unrestricted'); + $response = $this->get($url . http_build_query(['FolderID' => $folder->ID])); + $this->assertEquals( + 403, + $response->getStatusCode(), + 'Access denied for getting permission of folder unauthenticated' + ); + + $response = $this->get($url . http_build_query(['FolderID' => 0])); + $this->assertEquals( + 403, + $response->getStatusCode(), + 'Access denied for getting permission of root folder unauthenticated' + ); + + $response = $this->get($url . http_build_query(['FolderID' => -1])); + $this->assertEquals( + 403, + $response->getStatusCode(), + 'Access denied for getting permission of non-existent folder unauthenticated' + ); + + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + $adminOnlyFolder = Folder::find('admin-only'); + $response = $this->get($url . http_build_query(['FolderID' => $adminOnlyFolder->ID])); + $this->assertEquals( + 403, + $response->getStatusCode(), + 'Access denied for getting permission of Folder user does not have read access on' + ); + + $this->logInWithPermission('ADMIN'); + $adminOnlyFolder = Folder::find('admin-only'); + $response = $this->get($url . http_build_query(['FolderID' => $adminOnlyFolder->ID])); + $this->assertEquals( + 200, + $response->getStatusCode(), + 'Access denied for getting permission of Folder user does not have read access on' + ); + } + + public function testGetFolderPermissionNonExistentFolder() + { + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + $url = 'UserDefinedFormController/getfoldergrouppermissions?'; + + $response = $this->get($url . http_build_query(['FolderID' => -1])); + $this->assertEquals( + 400, + $response->getStatusCode(), + 'Non existent folder should fail' + ); + } + + public function testGetFolderPermissionValidRequest() + { + $url = 'UserDefinedFormController/getfoldergrouppermissions?'; + $this->logInWithPermission(['CMS_ACCESS_CMSMain', 'CMS_ACCESS_AssetAdmin']); + + $folder = Folder::find('unrestricted'); + $response = $this->get($url . http_build_query(['FolderID' => $folder->ID])); + $this->assertEquals( + 200, + $response->getStatusCode(), + 'Valid request is successfull' + ); + $this->assertContains('Unrestricted access, uploads will be visible to anyone', $response->getBody()); + + $folder = Folder::find('restricted-folder'); + $response = $this->get($url . http_build_query(['FolderID' => 0])); + $this->assertEquals( + 200, + $response->getStatusCode(), + 'Valid request for root folder is successful' + ); + $this->assertContains('Unrestricted access, uploads will be visible to anyone', $response->getBody()); + + $folder = Folder::find('restricted-folder'); + $response = $this->get($url . http_build_query(['FolderID' => $folder->ID])); + $this->assertEquals( + 200, + $response->getStatusCode(), + 'Valid request for root folder is successful' + ); + $this->assertContains('Restricted access, uploads will be visible to logged-in users ', $response->getBody()); + + $this->logInWithPermission('ADMIN'); + $adminOnlyFolder = Folder::find('admin-only'); + $response = $this->get($url . http_build_query(['FolderID' => $adminOnlyFolder->ID])); + $this->assertEquals( + 200, + $response->getStatusCode(), + 'Valid request for folder restricted to group is successful' + ); + $this->assertContains('Restricted access, uploads will be visible to the following groups: Administrators', $response->getBody()); + } + public function testImageThumbnailCreated() { Config::modify()->set(Upload_Validator::class, 'use_is_uploaded_file', false); @@ -419,4 +863,46 @@ class UserDefinedFormControllerTest extends FunctionalTest $store = Injector::inst()->get(AssetStore::class); $this->assertTrue($store->exists($image->getFilename(), $image->getHash(), 'FitMaxWzM1MiwyNjRd')); } + + public function testGetFormSubmissionFolder() + { + $submissionFolder = Folder::find('Form-submissions'); + $this->assertEmpty($submissionFolder, 'Submission folder does not exists initially.'); + + // No parameters + $submissionFolder = UserDefinedFormController::getFormSubmissionFolder(); + $this->assertNotEmpty($submissionFolder, 'Submission folder exists after getFormSubmissionFolder call'); + $this->assertEquals('Form-submissions/', $submissionFolder->getFilename(), 'Submission folder got created under correct name'); + + $this->assertEquals(InheritedPermissions::ONLY_THESE_USERS, $submissionFolder->CanViewType, 'Submission folder has correct permissions'); + $this->assertNotEmpty($submissionFolder->ViewerGroups()->find('Code', 'administrators'), 'Submission folder is limited to administrators'); + + // subfolder name + $submissionSubFolder = UserDefinedFormController::getFormSubmissionFolder('test-form'); + $this->assertNotEmpty($submissionSubFolder, 'Submission subfolder has been created'); + $this->assertEquals('Form-submissions/test-form/', $submissionSubFolder->getFilename(), 'Submission sub folder got created under correct name'); + $this->assertEquals(InheritedPermissions::INHERIT, $submissionSubFolder->CanViewType, 'Submission sub folder inherit permission from parent'); + + // make sure parent folder permission don't get overridden + $submissionFolder = Folder::find('Form-submissions'); + $submissionFolder->CanViewType = InheritedPermissions::INHERIT; + $submissionFolder->write(); + + $submissionSubFolder = UserDefinedFormController::getFormSubmissionFolder('test-form-2'); + $submissionFolder = Folder::find('Form-submissions'); + $this->assertEquals(InheritedPermissions::INHERIT, $submissionFolder->CanViewType, 'Submission sub folder inherit permission from parent'); + + // Submission folder get recreated + $submissionFolder->delete(); + $submissionFolder = Folder::find('Form-submissions'); + $this->assertEmpty($submissionFolder, 'Submission folder does has been deleted.'); + + $submissionSubFolder = UserDefinedFormController::getFormSubmissionFolder('test-form-3'); + $submissionFolder = Folder::find('Form-submissions'); + $this->assertNotEmpty($submissionFolder, 'Submission folder got recreated'); + $this->assertEquals('Form-submissions/', $submissionFolder->getFilename(), 'Submission folder got recreated under correct name'); + + $this->assertEquals(InheritedPermissions::ONLY_THESE_USERS, $submissionFolder->CanViewType, 'Submission folder has correct permissions'); + $this->assertNotEmpty($submissionFolder->ViewerGroups()->find('Code', 'administrators'), 'Submission folder is limited to administrators'); + } } diff --git a/tests/Model/EditableFormField/EditableFileFieldTest.php b/tests/Model/EditableFormField/EditableFileFieldTest.php index 12de5b9..72b21b0 100644 --- a/tests/Model/EditableFormField/EditableFileFieldTest.php +++ b/tests/Model/EditableFormField/EditableFileFieldTest.php @@ -2,6 +2,7 @@ namespace SilverStripe\UserForms\Tests\Model\EditableFormField; +use SilverStripe\Assets\Folder; use SilverStripe\Dev\SapphireTest; use SilverStripe\ORM\ValidationException; use SilverStripe\UserForms\Model\EditableFormField\EditableFileField; @@ -72,4 +73,40 @@ class EditableFileFieldTest extends SapphireTest $field->Name = 'EditableFormField_123456'; $this->assertEmpty($field->getFormField()->Title()); } + + public function testOnBeforeWrite() + { + $this->logOut(); + + /** @var EditableFileField $fileField */ + $fileField = $this->objFromFixture(EditableFileField::class, 'file-field'); + + $defaultFolder = Folder::find('Form-submissions'); + $this->assertNotEmpty($defaultFolder, 'Default Folder was created along with the EditableFileField'); + $this->assertFalse($defaultFolder->canView(), 'Default Folder default to being restricted'); + $this->assertFalse((boolean)$fileField->FolderConfirmed, 'EditableFileField are not Folder Confirmed initially'); + + $this->assertEquals( + $defaultFolder->ID, + $fileField->FolderID, + 'EditableFileField default to default form submission folder' + ); + + $fileField->FolderID = Folder::find_or_make('boom')->ID; + $fileField->write(); + $this->assertTrue( + (boolean)$fileField->FolderConfirmed, + 'EditableFileField are Folder Confirmed once you assigned them a folder' + ); + + $secondField = EditableFileField::create(); + $secondField->ParentID = $fileField->ParentID; + $secondField->write(); + + $this->assertEquals( + $fileField->FolderID, + $secondField->FolderID, + 'Second EditableFileField defaults to first field FolderID' + ); + } } diff --git a/tests/Model/EditableFormFieldTest.yml b/tests/Model/EditableFormFieldTest.yml index d5f8695..05e3795 100644 --- a/tests/Model/EditableFormFieldTest.yml +++ b/tests/Model/EditableFormFieldTest.yml @@ -1,3 +1,7 @@ +SilverStripe\Security\Group: + admin: + Title: Administrators + SilverStripe\UserForms\Model\EditableFormField\EditableTextField: basic-text: Name: basic_text_name @@ -311,3 +315,8 @@ SilverStripe\UserForms\Model\UserDefinedForm: - =>SilverStripe\UserForms\Model\EditableFormField\EditableTextField.basic-text-2 empty-form: Title: Empty Form + + form-with-file-upload: + Title: A Form with a file upload field + Fields: + - =>SilverStripe\UserForms\Model\EditableFormField\EditableFileField.file-field diff --git a/tests/UserFormsTest.yml b/tests/UserFormsTest.yml index 7d09258..46cfd30 100644 --- a/tests/UserFormsTest.yml +++ b/tests/UserFormsTest.yml @@ -1,3 +1,19 @@ +SilverStripe\Security\Group: + admin: + Title: Administrators + +SilverStripe\Assets\Folder: + unrestricted: + Name: unrestricted + CanViewType: Anyone + restricted: + Name: restricted-folder + CanViewType: LoggedInUsers + admin-only: + Name: admin-only + CanViewType: OnlyTheseUsers + ViewerGroups: =>SilverStripe\Security\Group.admin + SilverStripe\UserForms\Model\EditableFormField\EditableFormStep: form1step1: Title: 'Step 1' @@ -169,6 +185,10 @@ SilverStripe\UserForms\Model\EditableFormField\EditableTextField: Title: Summary Text Field ShowInSummary: false + become-file-upload: + Name: BecomeFileUpload + Title: I will be converted to a File upload field + SilverStripe\UserForms\Model\EditableFormField\EditableDropdown: basic-dropdown: Name: basic-dropdown @@ -251,6 +271,10 @@ SilverStripe\UserForms\Model\EditableFormField\EditableFileField: file-field-1: Name: 'file_field_name' Title: 'File field title' + Folder: =>SilverStripe\Assets\Folder.unrestricted + file-field-2: + Name: FileUploadField + Title: File Upload Field SilverStripe\UserForms\Model\EditableFormField\EditableFieldGroupEnd: group1end: @@ -442,6 +466,14 @@ SilverStripe\UserForms\Model\UserDefinedForm: Fields: - =>SilverStripe\UserForms\Model\EditableFormField\EditableDropdown.basic-dropdown + restricted-user-form: + Title: Form editable only by admin + CanEditType: OnlyTheseUsers + EditorGroups: =>SilverStripe\Security\Group.admin + Fields: + - =>SilverStripe\UserForms\Model\EditableFormField\EditableFileField.file-field-2 + - =>SilverStripe\UserForms\Model\EditableFormField\EditableTextField.become-file-upload + upload-form: Title: 'Form with upload field' Fields: