NEW Add folder confirmation modal when adding a new `EditableFileField` to a userform. (#972)

This commit is contained in:
Andre Kiste 2020-06-10 16:59:01 +12:00 committed by GitHub
parent 14ce51e517
commit 3b40be3b8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1209 additions and 15 deletions

View File

@ -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}});
!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<arguments.length;t++){var r=arguments[t];for(var o in r)Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o])}return e},s=r(4),i=o(s),a=r(0),l=o(a),h=r(2),u=o(h),c=r(3),d=o(c),f=r(1),p=r("./node_modules/url/url.js"),m=o(p),v=r(5),F=o(v),g=(0,f.loadComponent)("FormBuilderModal");l.default.entwine("ss",function(e){function t(t,r,o){t?(e(r).show(),e(o).hide()):(e(o).show(),e(r).hide())}e("#Form_EditForm_Fields").entwine({onmatch:function(){var e=this;this._super(),this.on("addnewinline",function(){e.one("reload",function(){e.find(".ss-gridfield-item").last().find(".col-ClassName select").attr("data-folderconfirmed",0)})})}}),e("#Form_ConfirmFolderForm_FolderOptions-new").entwine({onmatch:function(){e("#Form_ConfirmFolderForm_CreateFolder_Holder").detach().appendTo(e("#Form_ConfirmFolderForm_FolderOptions-new").parent().parent()),t(e(this).prop("checked"),"#Form_ConfirmFolderForm_CreateFolder_Holder","#Form_ConfirmFolderForm_FolderID_Holder")},onchange:function(){t(e(this).prop("checked"),"#Form_ConfirmFolderForm_CreateFolder_Holder","#Form_ConfirmFolderForm_FolderID_Holder")}}),e("#Form_ConfirmFolderForm_FolderOptions-existing").entwine({onmatch:function(){e("#Form_ConfirmFolderForm_FolderID_Holder").detach().appendTo(e("#Form_ConfirmFolderForm_FolderOptions-existing").parent().parent()),t(e(this).prop("checked"),"#Form_ConfirmFolderForm_FolderID_Holder","#Form_ConfirmFolderForm_CreateFolder_Holder")},onchange:function(){t(e(this).prop("checked"),"#Form_ConfirmFolderForm_FolderID_Holder","#Form_ConfirmFolderForm_CreateFolder_Holder")}}),e("#Form_ConfirmFolderForm_FolderID_Holder .treedropdownfield.is-open,#Form_ItemEditForm_FolderID .treedropdownfield.is-open").entwine({onunmatch:function(){var t=this,r=m.default.parse("UserDefinedFormController/getfoldergrouppermissions"),o=F.default.parse(r.query);o.FolderID=e(this).find("input[name=FolderID]").val();var s=m.default.format(n({},r,{search:F.default.stringify(o)}));return fetch(s,{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(r){return e(t).siblings(".form__field-description").html(r),e(t).parent().siblings(".form__field-description").html(r),r}).catch(function(e){l.default.noticeAdd({text:e.message,stay:!1,type:"error"})})}}),e(".uf-field-editor .ss-gridfield-items .dropdown.editable-column-field.form-group--no-label:not([data-folderconfirmed='1'])").entwine({onchange:function(){if("SilverStripe\\UserForms\\Model\\EditableFormField\\EditableFileField"===this.get(0).value&&!e(".uf-field-editor .ss-gridfield-items .dropdown.editable-column-field.form-group--no-label[data-folderconfirmed='1']").length){var t=e("#confirm-folder__dialog-wrapper");t.length&&t.remove(),t=e('<div id="confirm-folder__dialog-wrapper" />');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<s;)t=e.charCodeAt(n++),t>=55296&&t<=56319&&n<s?(r=e.charCodeAt(n++),56320==(64512&r)?o.push(((1023&t)<<10)+(1023&r)+65536):(o.push(t),n--)):o.push(t);return o}function h(e){return i(e,function(e){var t="";return e>65535&&(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<r;++o)e.charCodeAt(o)>=128&&s("not-basic"),m.push(e.charCodeAt(o));for(n=r>0?r+1:0;n<v;){for(i=F,a=1,l=_;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,!(c<f);l+=_)p=_-f,a>U(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<v;++a)(m=e[a])<128&&I.push(k(m));for(o=n=I.length,n&&I.push(x);o<v;){for(h=g,a=0;a<v;++a)(m=e[a])>=t&&m<h&&(h=m);for(F=o+1,h-t>U((g-r)/F)&&s("overflow"),r+=(h-t)*F,t=h,a=0;a<v;++a)if(m=e[a],m<t&&++r>g&&s("overflow"),m==t){for(u=r,f=_;p=f<=i?b:f>=i+y?y:f-i,!(u<p);f+=_)w=u-p,j=_-p,I.push(k(c(p+w%j,0))),u=U(w/j);I.push(k(c(u,0))),i=d(r,F,o==n),r=0,++o}++r,++t}return I.join("")}function m(e){return a(e,function(e){return I.test(e)?f(e.slice(4).toLowerCase()):e})}function v(e){return a(e,function(e){return E.test(e)?"xn--"+p(e):e})}var F,g=("object"==typeof t&&t&&t.nodeType,"object"==typeof e&&e&&e.nodeType,2147483647),_=36,b=1,y=26,j=38,w=700,C=72,O=128,x="-",I=/^xn--/,E=/[^\x20-\x7E]/,A=/[\x2E\u3002\uFF0E\uFF61]/g,S={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 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<h;++u){var c,d,f,p,m=e[u].replace(a,"%20"),v=m.indexOf(r);v>=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<e.length;o++)r.push(t(e[o],o));return r}var n=function(e){switch(typeof e){case"string":return e;case"boolean":return e?"true":"false";case"number":return isFinite(e)?e:"";default:return""}};e.exports=function(e,t,r,a){return t=t||"&",r=r||"=",null===e&&(e=void 0),"object"==typeof e?o(i(e),function(i){var a=encodeURIComponent(n(i))+r;return s(e[i])?o(e[i],function(e){return a+encodeURIComponent(n(e))}).join(t):a+encodeURIComponent(n(e[i]))}).join(t):a?encodeURIComponent(n(a))+r+encodeURIComponent(n(e)):""};var s=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},i=Object.keys||function(e){var t=[];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.push(r);return t}},"./node_modules/querystring-es3/index.js":function(e,t,r){"use strict";t.decode=t.parse=r("./node_modules/querystring-es3/decode.js"),t.encode=t.stringify=r("./node_modules/querystring-es3/encode.js")},"./node_modules/url/url.js":function(e,t,r){"use strict";function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function n(e,t,r){if(e&&h.isObject(e)&&e instanceof o)return e;var n=new o;return n.parse(e,t,r),n}function s(e){return h.isString(e)&&(e=n(e)),e instanceof o?e.format():o.prototype.format.call(e)}function i(e,t){return n(e,!1,!0).resolve(t)}function a(e,t){return e?n(e,!1,!0).resolveObject(t):t}var l=r("./node_modules/punycode/punycode.js"),h=r("./node_modules/url/util.js");t.parse=n,t.resolve=i,t.resolveObject=a,t.format=s,t.Url=o;var u=/^([a-z0-9.+-]+:)/i,c=/:[0-9]*$/,d=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,f=["<",">",'"',"`"," ","\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&&o<e.indexOf("#")?"?":"#",s=e.split(n),i=/\\/g;s[0]=s[0].replace(i,"/"),e=s.join(n);var a=e;if(a=a.trim(),!r&&1===e.split("#").length){var c=d.exec(a);if(c)return this.path=a,this.href=a,this.pathname=c[1],c[2]?(this.search=c[2],this.query=t?w.parse(this.search.substr(1)):this.search.substr(1)):t&&(this.search="",this.query={}),this}var f=u.exec(a);if(f){f=f[0];var p=f.toLowerCase();this.protocol=p,a=a.substr(f.length)}if(r||f||a.match(/^\/\/[^@\/]+@[^@\/]+/)){var C="//"===a.substr(0,2);!C||f&&y[f]||(a=a.substr(2),this.slashes=!0)}if(!y[f]&&(C||f&&!j[f])){for(var O=-1,x=0;x<F.length;x++){var I=a.indexOf(F[x]);-1!==I&&(-1===O||I<O)&&(O=I)}var E,A;A=-1===O?a.lastIndexOf("@"):a.lastIndexOf("@",O),-1!==A&&(E=a.slice(0,A),a=a.slice(A+1),this.auth=decodeURIComponent(E)),O=-1;for(var x=0;x<v.length;x++){var I=a.indexOf(v[x]);-1!==I&&(-1===O||I<O)&&(O=I)}-1===O&&(O=a.length),this.host=a.slice(0,O),a=a.slice(O),this.parseHost(),this.hostname=this.hostname||"";var S="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!S)for(var q=this.hostname.split(/\./),x=0,U=q.length;x<U;x++){var k=q[x];if(k&&!k.match(g)){for(var M="",R=0,N=k.length;R<N;R++)k.charCodeAt(R)>127?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;x<U;x++){var G=m[x];if(-1!==a.indexOf(G)){var $=encodeURIComponent(G);$===G&&($=escape(G)),a=a.split(G).join($)}}var z=a.indexOf("#");-1!==z&&(this.hash=a.substr(z),a=a.slice(0,z));var B=a.indexOf("?");if(-1!==B?(this.search=a.substr(B),this.query=a.substr(B+1),t&&(this.query=w.parse(this.query)),a=a.slice(0,B)):t&&(this.search="",this.query={}),a&&(this.pathname=a),j[p]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){var P=this.pathname||"",K=this.search||"";this.path=P+K}return this.href=this.format(),this},o.prototype.format=function(){var e=this.auth||"";e&&(e=encodeURIComponent(e),e=e.replace(/%3A/i,":"),e+="@");var t=this.protocol||"",r=this.pathname||"",o=this.hash||"",n=!1,s="";this.host?n=e+this.host:this.hostname&&(n=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(n+=":"+this.port)),this.query&&h.isObject(this.query)&&Object.keys(this.query).length&&(s=w.stringify(this.query));var i=this.search||s&&"?"+s||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||j[t])&&!1!==n?(n="//"+(n||""),r&&"/"!==r.charAt(0)&&(r="/"+r)):n||(n=""),o&&"#"!==o.charAt(0)&&(o="#"+o),i&&"?"!==i.charAt(0)&&(i="?"+i),r=r.replace(/[?#]/g,function(e){return encodeURIComponent(e)}),i=i.replace("#","%23"),t+n+r+i+o},o.prototype.resolve=function(e){return this.resolveObject(n(e,!1,!0)).format()},o.prototype.resolveObject=function(e){if(h.isString(e)){var t=new o;t.parse(e,!1,!0),e=t}for(var r=new o,n=Object.keys(this),s=0;s<n.length;s++){var i=n[s];r[i]=this[i]}if(r.hash=e.hash,""===e.href)return r.href=r.format(),r;if(e.slashes&&!e.protocol){for(var a=Object.keys(e),l=0;l<a.length;l++){var u=a[l];"protocol"!==u&&(r[u]=e[u])}return j[r.protocol]&&r.hostname&&!r.pathname&&(r.path=r.pathname="/"),r.href=r.format(),r}if(e.protocol&&e.protocol!==r.protocol){if(!j[e.protocol]){for(var c=Object.keys(e),d=0;d<c.length;d++){var f=c[d];r[f]=e[f]}return r.href=r.format(),r}if(r.protocol=e.protocol,e.host||y[e.protocol])r.pathname=e.pathname;else{for(var p=(e.pathname||"").split("/");p.length&&!(e.host=p.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==p[0]&&p.unshift(""),p.length<2&&p.unshift(""),r.pathname=p.join("/")}if(r.search=e.search,r.query=e.query,r.host=e.host||"",r.auth=e.auth,r.hostname=e.hostname||e.host,r.port=e.port,r.pathname||r.search){var m=r.pathname||"",v=r.search||"";r.path=m+v}return r.slashes=r.slashes||e.slashes,r.href=r.format(),r}var F=r.pathname&&"/"===r.pathname.charAt(0),g=e.host||e.pathname&&"/"===e.pathname.charAt(0),_=g||F||r.host&&e.pathname,b=_,w=r.pathname&&r.pathname.split("/")||[],p=e.pathname&&e.pathname.split("/")||[],C=r.protocol&&!j[r.protocol];if(C&&(r.hostname="",r.port=null,r.host&&(""===w[0]?w[0]=r.host:w.unshift(r.host)),r.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===p[0]?p[0]=e.host:p.unshift(e.host)),e.host=null),_=_&&(""===p[0]||""===w[0])),g)r.host=e.host||""===e.host?e.host:r.host,r.hostname=e.hostname||""===e.hostname?e.hostname:r.hostname,r.search=e.search,r.query=e.query,w=p;else if(p.length)w||(w=[]),w.pop(),w=w.concat(p),r.search=e.search,r.query=e.query;else if(!h.isNullOrUndefined(e.search)){if(C){r.hostname=r.host=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.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}});

View File

@ -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."
});
}
}

View File

@ -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"
}
"UserForms.SHOW_OPTIONS": "Show options",
"UserForms.FILE_CONFIRMATION_TITLE": "Select file upload folder",
"UserForms.FILE_CONFIRMATION_CONFIRMATION": "Folder confirmed successfully."
}

View File

@ -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 = $('<div id="confirm-folder__dialog-wrapper" />');
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(
<FormBuilderModal
title={title}
isOpen={isOpen}
onSubmit={handleSubmit}
onClosed={handleHide}
schemaUrl={schemaUrl}
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() {
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();
}
});
});

View File

@ -1,3 +1,4 @@
// Used for CMS form fields
import 'bundles/FieldEditor';
import 'bundles/ConfirmFolder';
import 'bundles/Recipient';

View File

@ -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 <i>File upload field.</i>'));
$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;
}
}

View File

@ -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) {

View File

@ -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(
'<span class="icon %s form-description__icon" aria-hidden="true"></span>%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;
}
}
}

View File

@ -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 <i>File upload field.</i>'
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:'

View File

@ -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": [

View File

@ -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');
}
}

View File

@ -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'
);
}
}

View File

@ -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

View File

@ -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: