From e0d0047bf792a4375593be9d51e8d295726eae89 Mon Sep 17 00:00:00 2001 From: Andreas Fernandez <a.fernandez@scripting-base.de> Date: Thu, 6 Jul 2023 11:38:00 +0200 Subject: [PATCH] [BUGFIX] Accept "00:00 01-01-1970" in FormEngine UI The FormEngine validation now accepts "00:00 01-01-1970" as an entered date, which evaluates to int 0. If string 0 is passed, the field is supposed to be empty. Previously, a too simple negation check was in place. In the long run, the whole date validation handling needs a major rewrite to not rely on timestamps anymore. Resolves: #101258 Releases: main, 12.4 Change-Id: I90a976088e01c23443369eff76f4c6401463715e Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/79819 Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de> Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de> Tested-by: core-ci <typo3@b13.com> --- .../TypeScript/backend/form-engine-validation.ts | 16 +++++++++++----- .../backend/tests/form-engine-validation-test.ts | 15 +++++++++++---- .../Public/JavaScript/form-engine-validation.js | 2 +- .../JavaScript/form-engine-validation-test.js | 2 +- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Build/Sources/TypeScript/backend/form-engine-validation.ts b/Build/Sources/TypeScript/backend/form-engine-validation.ts index 42924772bd06..7df1300bd5b2 100644 --- a/Build/Sources/TypeScript/backend/form-engine-validation.ts +++ b/Build/Sources/TypeScript/backend/form-engine-validation.ts @@ -154,8 +154,11 @@ export default (function() { const date = DateTime.fromISO(value.toString(), { zone: 'utc' }); theString = date.toFormat('dd-MM-yyyy'); } else { + if (value === '' || value === '0') { + return ''; + } parsedInt = parseInt(value.toString(), 10); - if (!parsedInt) { + if (isNaN(parsedInt)) { return ''; } theTime = new Date(parsedInt * 1000); @@ -166,11 +169,11 @@ export default (function() { } break; case 'datetime': - // eslint-disable-next-line radix - if (value.toString().indexOf('-') <= 0 && !(typeof value === 'number' ? value : parseInt(value))) { + if (value === '' || value === '0') { + // if value is '0' (string), it's supposed to be empty return ''; } - theString = FormEngineValidation.formatValue('time', value, config) + ' ' + FormEngineValidation.formatValue('date', value, config); + theString = (FormEngineValidation.formatValue('time', value, config) + ' ' + FormEngineValidation.formatValue('date', value, config)).trim(); break; case 'time': case 'timesec': @@ -178,9 +181,12 @@ export default (function() { if (value.toString().indexOf('-') > 0) { dateValue = DateTime.fromISO(value.toString(), { zone: 'utc' }); } else { + if (value === '' || value === '0') { + return ''; + } // eslint-disable-next-line radix parsedInt = typeof value === 'number' ? value : parseInt(value); - if (!parsedInt && value.toString() !== '0') { + if (isNaN(parsedInt)) { return ''; } dateValue = DateTime.fromSeconds(parsedInt, { zone: 'utc' }); diff --git a/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts b/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts index 8b2398f81c6e..3f0faa4b0bfb 100644 --- a/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts +++ b/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts @@ -25,12 +25,19 @@ interface ProcessValueData { describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => { const formatValueDataProvider: Array<FormatValueData> = [ { - 'description': 'works for type date', + 'description': 'returns empty string with string 0', 'type': 'date', - 'value': 0, + 'value': '0', 'config': {}, 'result': '' }, + { + 'description': 'returns date with int 0', + 'type': 'date', + 'value': 0, + 'config': {}, + 'result': '01-01-1970' + }, { 'description': 'works for type date with timestamp', 'type': 'date', @@ -48,7 +55,7 @@ describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => { { 'description': 'works for type datetime', 'type': 'datetime', - 'value': 0, + 'value': '0', 'config': {}, 'result': '' }, @@ -69,7 +76,7 @@ describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => { { 'description': 'resolves to empty result for zero value', 'type': 'datetime', - 'value': 0, + 'value': '0', 'config': {}, 'result': '' }, diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js index 61533c4c110e..375fbf2861b9 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"},customEvaluations=new Map;return FormEngineValidation.initialize=function(){$(document).find("."+FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass),FormEngineValidation.initializeInputFields().promise().done((function(){$(document).on("change",FormEngineValidation.rulesSelector,(e=>{FormEngineValidation.validateField(e.currentTarget),FormEngineValidation.markFieldAsChanged(e.currentTarget)})),FormEngineValidation.registerSubmitCallback()}));const e=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(e),FormEngineValidation.lastDate=FormEngineValidation.getDate(e),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){return $(document).find(FormEngineValidation.inputSelector).each((function(e,n){const t=$(n).data("formengine-input-params"),a=t.field,i=$('[name="'+a+'"]');void 0===i.data("main-field")&&(i.data("main-field",a),i.data("config",t),FormEngineValidation.initializeInputField(a))}))},FormEngineValidation.initializeInputField=function(e){const n=$('[name="'+e+'"]'),t=$('[data-formengine-input-name="'+e+'"]');let a=$('[name="'+n.data("main-field")+'"]');0===a.length&&(a=n);const i=a.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let a=n.val();for(let n=0;n<e.length;n++)a=FormEngineValidation.formatValue(e[n],a,i);a.length&&t.val(a)}t.data("main-field",e),t.data("config",i),t.on("change",(function(){FormEngineValidation.updateInputField(t.attr("data-formengine-input-name"))})),t.attr("data-formengine-input-initialized","true")},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(a=parseInt(n.toString(),10),!a)return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(n.toString().indexOf("-")<=0&&!("number"==typeof n?n:parseInt(n)))return"";o=FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t);break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(a="number"==typeof n?n:parseInt(n),!a&&"0"!==n.toString())return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=$('[name="'+e+'"]');let t=$('[name="'+n.data("main-field")+'"]');0===t.length&&(t=n);const a=$('[data-formengine-input-name="'+t.attr("name")+'"]'),i=t.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let n=a.val();for(let t=0;t<e.length;t++)n=FormEngineValidation.processValue(e[t],n,i);let o=n;for(let n=0;n<e.length;n++)o=FormEngineValidation.formatValue(e[n],o,i);t.val(n),t.get(0).dispatchEvent(new Event("change")),a.val(o)}},FormEngineValidation.validateField=function(e,n){const t=e instanceof $?e.get(0):e;if(n=n||t.value||"",void 0===t.dataset.formengineValidationRules)return n;const a=JSON.parse(t.dataset.formengineValidationRules);let i=!1,o=0;const r=n;let l,s,m;Array.isArray(n)||(n=n.trimStart());for(const e of a){if(i)break;switch(e.type){case"required":""===n&&(i=!0,t.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((e.minItems||e.maxItems)&&(l=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:parseInt(t.value,10),void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0))),void 0!==e.lower){const t=1*e.lower;!isNaN(t)&&parseInt(n,10)<t&&(i=!0)}if(void 0!==e.upper){const t=1*e.upper;!isNaN(t)&&parseInt(n,10)>t&&(i=!0)}}break;case"select":case"category":(e.minItems||e.maxItems)&&(l=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:t instanceof HTMLSelectElement?t.querySelectorAll("option:checked").length:t.querySelectorAll("input[value]:checked").length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"group":case"folder":case"inline":(e.minItems||e.maxItems)&&(o=FormEngineValidation.trimExplode(",",t.value).length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"min":(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length>0&&t.value.length<t.minLength&&(i=!0)}}const d=!i,c=t.closest(FormEngineValidation.markerSelector);return null!==c&&c.classList.toggle(FormEngineValidation.errorClass,!d),FormEngineValidation.markParentTab($(t),d),$(document).trigger("t3-formengine-postfieldvalidation"),r},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&$(document).find(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").removeClass(FormEngineValidation.errorClass).removeClass("has-validation-error");const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector)){const n=$(e);if(!n.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted").length){let e=!1;const t=n.val(),a=FormEngineValidation.validateField(n,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)e=!0;else for(let n=0;n<a.length;n++)if(a[n]!==t[n]){e=!0;break}}else a.length&&t!==a&&(e=!0);e&&n.val(a)}}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.trimExplode=function(e,n){const t=[],a=n.split(e);for(let e=0;e<a.length;e++){const n=a[e].trim();n.length>0&&t.push(n)}return t},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a="-"===t.substring(0,1);t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){e.parents(".tab-pane").each((function(e,t){const a=$(t);n&&(n=0===a.find(".has-error").length);const i=a.attr("id");$(document).find('a[href="#'+i+'"]').closest(".t3js-tabmenu-item").toggleClass("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((function(e){if($("."+FormEngineValidation.errorClass).length>0){const n=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);n.addEventListener("button.clicked",(()=>n.hideModal())),e.stopImmediatePropagation()}}))},FormEngineValidation}()); \ No newline at end of file +import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"},customEvaluations=new Map;return FormEngineValidation.initialize=function(){$(document).find("."+FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass),FormEngineValidation.initializeInputFields().promise().done((function(){$(document).on("change",FormEngineValidation.rulesSelector,(e=>{FormEngineValidation.validateField(e.currentTarget),FormEngineValidation.markFieldAsChanged(e.currentTarget)})),FormEngineValidation.registerSubmitCallback()}));const e=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(e),FormEngineValidation.lastDate=FormEngineValidation.getDate(e),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){return $(document).find(FormEngineValidation.inputSelector).each((function(e,n){const t=$(n).data("formengine-input-params"),a=t.field,i=$('[name="'+a+'"]');void 0===i.data("main-field")&&(i.data("main-field",a),i.data("config",t),FormEngineValidation.initializeInputField(a))}))},FormEngineValidation.initializeInputField=function(e){const n=$('[name="'+e+'"]'),t=$('[data-formengine-input-name="'+e+'"]');let a=$('[name="'+n.data("main-field")+'"]');0===a.length&&(a=n);const i=a.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let a=n.val();for(let n=0;n<e.length;n++)a=FormEngineValidation.formatValue(e[n],a,i);a.length&&t.val(a)}t.data("main-field",e),t.data("config",i),t.on("change",(function(){FormEngineValidation.updateInputField(t.attr("data-formengine-input-name"))})),t.attr("data-formengine-input-initialized","true")},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(""===n||"0"===n)return"";if(a=parseInt(n.toString(),10),isNaN(a))return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(""===n||"0"===n)return"";o=(FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t)).trim();break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(""===n||"0"===n)return"";if(a="number"==typeof n?n:parseInt(n),isNaN(a))return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=$('[name="'+e+'"]');let t=$('[name="'+n.data("main-field")+'"]');0===t.length&&(t=n);const a=$('[data-formengine-input-name="'+t.attr("name")+'"]'),i=t.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let n=a.val();for(let t=0;t<e.length;t++)n=FormEngineValidation.processValue(e[t],n,i);let o=n;for(let n=0;n<e.length;n++)o=FormEngineValidation.formatValue(e[n],o,i);t.val(n),t.get(0).dispatchEvent(new Event("change")),a.val(o)}},FormEngineValidation.validateField=function(e,n){const t=e instanceof $?e.get(0):e;if(n=n||t.value||"",void 0===t.dataset.formengineValidationRules)return n;const a=JSON.parse(t.dataset.formengineValidationRules);let i=!1,o=0;const r=n;let l,s,m;Array.isArray(n)||(n=n.trimStart());for(const e of a){if(i)break;switch(e.type){case"required":""===n&&(i=!0,t.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((e.minItems||e.maxItems)&&(l=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:parseInt(t.value,10),void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0))),void 0!==e.lower){const t=1*e.lower;!isNaN(t)&&parseInt(n,10)<t&&(i=!0)}if(void 0!==e.upper){const t=1*e.upper;!isNaN(t)&&parseInt(n,10)>t&&(i=!0)}}break;case"select":case"category":(e.minItems||e.maxItems)&&(l=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),o=l.length?FormEngineValidation.trimExplode(",",l.val()).length:t instanceof HTMLSelectElement?t.querySelectorAll("option:checked").length:t.querySelectorAll("input[value]:checked").length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"group":case"folder":case"inline":(e.minItems||e.maxItems)&&(o=FormEngineValidation.trimExplode(",",t.value).length,void 0!==e.minItems&&(s=1*e.minItems,!isNaN(s)&&o<s&&(i=!0)),void 0!==e.maxItems&&(m=1*e.maxItems,!isNaN(m)&&o>m&&(i=!0)));break;case"min":(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length>0&&t.value.length<t.minLength&&(i=!0)}}const d=!i,c=t.closest(FormEngineValidation.markerSelector);return null!==c&&c.classList.toggle(FormEngineValidation.errorClass,!d),FormEngineValidation.markParentTab($(t),d),$(document).trigger("t3-formengine-postfieldvalidation"),r},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&$(document).find(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").removeClass(FormEngineValidation.errorClass).removeClass("has-validation-error");const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector)){const n=$(e);if(!n.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted").length){let e=!1;const t=n.val(),a=FormEngineValidation.validateField(n,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)e=!0;else for(let n=0;n<a.length;n++)if(a[n]!==t[n]){e=!0;break}}else a.length&&t!==a&&(e=!0);e&&n.val(a)}}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.trimExplode=function(e,n){const t=[],a=n.split(e);for(let e=0;e<a.length;e++){const n=a[e].trim();n.length>0&&t.push(n)}return t},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a="-"===t.substring(0,1);t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){e.parents(".tab-pane").each((function(e,t){const a=$(t);n&&(n=0===a.find(".has-error").length);const i=a.attr("id");$(document).find('a[href="#'+i+'"]').closest(".t3js-tabmenu-item").toggleClass("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((function(e){if($("."+FormEngineValidation.errorClass).length>0){const n=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);n.addEventListener("button.clicked",(()=>n.hideModal())),e.stopImmediatePropagation()}}))},FormEngineValidation}()); \ No newline at end of file diff --git a/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js b/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js index a37f8d9b156c..135fb8d8a5ab 100644 --- a/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js +++ b/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import FormEngineValidation from"@typo3/backend/form-engine-validation.js";describe("TYPO3/CMS/Backend/FormEngineValidationTest:",(()=>{const e=[{description:"works for type date",type:"date",value:0,config:{},result:""},{description:"works for type date with timestamp",type:"date",value:1e7,config:{},result:"26-04-1970"},{description:"works for type date with iso date",type:"date",value:"2016-12-02T11:16:06+00:00",config:{},result:"02-12-2016"},{description:"works for type datetime",type:"datetime",value:0,config:{},result:""},{description:"works for type datetime with timestamp",type:"datetime",value:1e7,config:{},result:"17:46 26-04-1970"},{description:"works for type datetime with iso date",type:"datetime",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16 02-12-2016"},{description:"resolves to empty result for zero value",type:"datetime",value:0,config:{},result:""},{description:"resolves to empty result for invalid value",type:"datetime",value:"invalid",config:{},result:""},{description:"works for type time",type:"time",value:0,config:{},result:"00:00"},{description:"works for type time with timestamp",type:"time",value:1e7,config:{},result:"17:46"},{description:"works for type time with iso date",type:"time",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16"}];describe("tests for formatValue",(()=>{using(e,(function(e){it(e.description,(()=>{FormEngineValidation.initialize();const t=FormEngineValidation.formatValue(e.type,e.value,e.config);expect(t).toBe(e.result)}))}))}));const t=[{description:"works for command alpha with numeric value",command:"alpha",value:"1234",config:{},result:""},{description:"works for command alpha with string value",command:"alpha",value:"abc",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"abc123",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"123abc123",config:{},result:"abc"}];describe("test for processValue",(()=>{using(t,(function(e){it(e.description,(()=>{const t=FormEngineValidation.processValue(e.command,e.value,e.config);expect(t).toBe(e.result)}))}))})),describe("tests for trimExplode",(()=>{it("works for comma as separator and list without spaces",(()=>{expect(FormEngineValidation.trimExplode(",","foo,bar,baz")).toEqual(["foo","bar","baz"])})),it("works for comma as separator and list with spaces",(()=>{expect(FormEngineValidation.trimExplode(","," foo , bar , baz ")).toEqual(["foo","bar","baz"])})),it("works for pipe as separator and list with spaces",(()=>{expect(FormEngineValidation.trimExplode("|"," foo | bar | baz ")).toEqual(["foo","bar","baz"])}))})),describe("tests for parseInt",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseInt(0)).toBe(0)})),it("works for value 1",(()=>{expect(FormEngineValidation.parseInt(1)).toBe(1)})),it("works for value -1",(()=>{expect(FormEngineValidation.parseInt(-1)).toBe(-1)})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseInt("0")).toBe(0)})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseInt("1")).toBe(1)})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseInt("-1")).toBe(-1)})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseInt(.5)).toBe(0)})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseInt("0.5")).toBe(0)})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseInt("foo")).toBe(0)})),it("works for value true",(()=>{expect(FormEngineValidation.parseInt(!0)).toBe(0)})),it("works for value false",(()=>{expect(FormEngineValidation.parseInt(!1)).toBe(0)})),it("works for value null",(()=>{expect(FormEngineValidation.parseInt(null)).toBe(0)}))})),describe("tests for parseDouble",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseDouble(0)).toBe("0.00")})),it("works for value 1",(()=>{expect(FormEngineValidation.parseDouble(1)).toBe("1.00")})),it("works for value -1",(()=>{expect(FormEngineValidation.parseDouble(-1)).toBe("-1.00")})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseDouble("0")).toBe("0.00")})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseDouble("1")).toBe("1.00")})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseDouble("-1")).toBe("-1.00")})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseDouble(.5)).toBe("0.50")})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseDouble("0.5")).toBe("0.50")})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseDouble("foo")).toBe("0.00")})),it("works for value true",(()=>{expect(FormEngineValidation.parseDouble(!0)).toBe("0.00")})),it("works for value false",(()=>{expect(FormEngineValidation.parseDouble(!1)).toBe("0.00")})),it("works for value null",(()=>{expect(FormEngineValidation.parseDouble(null)).toBe("0.00")}))})),describe("tests for getYear",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),it("works for current date",(()=>{const e=new Date;expect(FormEngineValidation.getYear(e)).toBe(e.getFullYear())})),it("works for year 2013",(()=>{const e=new Date(2013,9,23);jasmine.clock().mockDate(e),expect(FormEngineValidation.getYear(e)).toBe(2013)}))})),describe("tests for getDate",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),xit("works for year 2013",(()=>{const e=new Date(2013,9,23,13,13,13);jasmine.clock().mockDate(e),expect(FormEngineValidation.getDate(e)).toBe(1382479200)}))}))})); \ No newline at end of file +import FormEngineValidation from"@typo3/backend/form-engine-validation.js";describe("TYPO3/CMS/Backend/FormEngineValidationTest:",(()=>{const e=[{description:"returns empty string with string 0",type:"date",value:"0",config:{},result:""},{description:"returns date with int 0",type:"date",value:0,config:{},result:"01-01-1970"},{description:"works for type date with timestamp",type:"date",value:1e7,config:{},result:"26-04-1970"},{description:"works for type date with iso date",type:"date",value:"2016-12-02T11:16:06+00:00",config:{},result:"02-12-2016"},{description:"works for type datetime",type:"datetime",value:"0",config:{},result:""},{description:"works for type datetime with timestamp",type:"datetime",value:1e7,config:{},result:"17:46 26-04-1970"},{description:"works for type datetime with iso date",type:"datetime",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16 02-12-2016"},{description:"resolves to empty result for zero value",type:"datetime",value:"0",config:{},result:""},{description:"resolves to empty result for invalid value",type:"datetime",value:"invalid",config:{},result:""},{description:"works for type time",type:"time",value:0,config:{},result:"00:00"},{description:"works for type time with timestamp",type:"time",value:1e7,config:{},result:"17:46"},{description:"works for type time with iso date",type:"time",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16"}];describe("tests for formatValue",(()=>{using(e,(function(e){it(e.description,(()=>{FormEngineValidation.initialize();const t=FormEngineValidation.formatValue(e.type,e.value,e.config);expect(t).toBe(e.result)}))}))}));const t=[{description:"works for command alpha with numeric value",command:"alpha",value:"1234",config:{},result:""},{description:"works for command alpha with string value",command:"alpha",value:"abc",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"abc123",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"123abc123",config:{},result:"abc"}];describe("test for processValue",(()=>{using(t,(function(e){it(e.description,(()=>{const t=FormEngineValidation.processValue(e.command,e.value,e.config);expect(t).toBe(e.result)}))}))})),describe("tests for trimExplode",(()=>{it("works for comma as separator and list without spaces",(()=>{expect(FormEngineValidation.trimExplode(",","foo,bar,baz")).toEqual(["foo","bar","baz"])})),it("works for comma as separator and list with spaces",(()=>{expect(FormEngineValidation.trimExplode(","," foo , bar , baz ")).toEqual(["foo","bar","baz"])})),it("works for pipe as separator and list with spaces",(()=>{expect(FormEngineValidation.trimExplode("|"," foo | bar | baz ")).toEqual(["foo","bar","baz"])}))})),describe("tests for parseInt",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseInt(0)).toBe(0)})),it("works for value 1",(()=>{expect(FormEngineValidation.parseInt(1)).toBe(1)})),it("works for value -1",(()=>{expect(FormEngineValidation.parseInt(-1)).toBe(-1)})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseInt("0")).toBe(0)})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseInt("1")).toBe(1)})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseInt("-1")).toBe(-1)})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseInt(.5)).toBe(0)})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseInt("0.5")).toBe(0)})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseInt("foo")).toBe(0)})),it("works for value true",(()=>{expect(FormEngineValidation.parseInt(!0)).toBe(0)})),it("works for value false",(()=>{expect(FormEngineValidation.parseInt(!1)).toBe(0)})),it("works for value null",(()=>{expect(FormEngineValidation.parseInt(null)).toBe(0)}))})),describe("tests for parseDouble",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseDouble(0)).toBe("0.00")})),it("works for value 1",(()=>{expect(FormEngineValidation.parseDouble(1)).toBe("1.00")})),it("works for value -1",(()=>{expect(FormEngineValidation.parseDouble(-1)).toBe("-1.00")})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseDouble("0")).toBe("0.00")})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseDouble("1")).toBe("1.00")})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseDouble("-1")).toBe("-1.00")})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseDouble(.5)).toBe("0.50")})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseDouble("0.5")).toBe("0.50")})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseDouble("foo")).toBe("0.00")})),it("works for value true",(()=>{expect(FormEngineValidation.parseDouble(!0)).toBe("0.00")})),it("works for value false",(()=>{expect(FormEngineValidation.parseDouble(!1)).toBe("0.00")})),it("works for value null",(()=>{expect(FormEngineValidation.parseDouble(null)).toBe("0.00")}))})),describe("tests for getYear",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),it("works for current date",(()=>{const e=new Date;expect(FormEngineValidation.getYear(e)).toBe(e.getFullYear())})),it("works for year 2013",(()=>{const e=new Date(2013,9,23);jasmine.clock().mockDate(e),expect(FormEngineValidation.getYear(e)).toBe(2013)}))})),describe("tests for getDate",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),xit("works for year 2013",(()=>{const e=new Date(2013,9,23,13,13,13);jasmine.clock().mockDate(e),expect(FormEngineValidation.getDate(e)).toBe(1382479200)}))}))})); \ No newline at end of file -- GitLab