From 0f24df96fd8bb08730d6d4b0354f7b8317db9232 Mon Sep 17 00:00:00 2001
From: Benjamin Franzke <ben@bnf.dev>
Date: Wed, 5 Jul 2023 23:05:17 +0200
Subject: [PATCH] [BUGFIX] Fix workspace stage-change mail recipient selection

Stage-change notification mails have only been delivered to the last
recipient off the list of possible recipients, because
Utility.convertFormToObject failed to detect checkbox elements
from (other) frames.
In this case the to-be-converted form elements are created on the top
frame (modal contents are placed outside list frame on the top frame).

Due to JavaScript being prototype based, instanceof checks do
only operate per-frame. Prototypes are bound to the current window.
`foo instanceof HTMLInputElement` is basically a shortcut
and equivalent to `foo instanceof window.HTMLInputElement`
while `window != top.window`.

Instead of `instanceof` checks, we switch to a `tagName` comparision
which is usable for cross-frame HTMLElement type detection.

Resolves: #99784
Releases: main, 12.4, 11.5
Change-Id: If9bfd7be1e46d8c04619be3563d3bdba9d9de549
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/79953
Tested-by: Benjamin Franzke <ben@bnf.dev>
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Benjamin Franzke <ben@bnf.dev>
---
 Build/Sources/TypeScript/backend/utility.ts                 | 5 +++--
 typo3/sysext/backend/Resources/Public/JavaScript/utility.js | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/utility.ts b/Build/Sources/TypeScript/backend/utility.ts
index c4dfd2ada2a1..e99b3d3cdcd8 100644
--- a/Build/Sources/TypeScript/backend/utility.ts
+++ b/Build/Sources/TypeScript/backend/utility.ts
@@ -129,11 +129,12 @@ class Utility {
       const value = element.value;
 
       if (name) {
-        if (element instanceof HTMLInputElement && element.type == 'checkbox') {
+        if (element.tagName.toLowerCase() === 'input' && element.type == 'checkbox') {
+          const checkbox = element as HTMLInputElement;
           if (obj[name] === undefined) {
             obj[name] = [];
           }
-          if (element.checked){
+          if (checkbox.checked){
             obj[name].push(value);
           }
 
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/utility.js b/typo3/sysext/backend/Resources/Public/JavaScript/utility.js
index 3e4827278535..a6c1688d14e4 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/utility.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/utility.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-class Utility{static trimExplode(t,e){return e.split(t).map((t=>t.trim())).filter((t=>""!==t))}static trimItems(t){return t.map((t=>t instanceof String?t.trim():t))}static intExplode(t,e,r=!1){return e.split(t).map((t=>parseInt(t,10))).filter((t=>!isNaN(t)||r&&0===t))}static isNumber(t){return!isNaN(parseFloat(t.toString()))&&isFinite(t)}static getParameterFromUrl(t,e){if(console.warn("The function `getParameterFromUrl()` of `@typo3/backend/utility` has been marked as deprecated and will be removed in TYPO3 v13. Use `new URL(url, window.location.origin).searchParams.get(parameter)` instead."),"function"!=typeof t.split)return"";const r=t.split("?");let i="";if(r.length>=2){const t=r.join("?"),n=encodeURIComponent(e)+"=",a=t.split(/[&;]/g);for(let t=a.length;t-- >0;)if(-1!==a[t].lastIndexOf(n,0)){i=a[t].split("=")[1];break}}return i}static updateQueryStringParameter(t,e,r){const i=new RegExp("([?&])"+e+"=.*?(&|$)","i"),n=t.includes("?")?"&":"?";return t.match(i)?t.replace(i,"$1"+e+"="+r+"$2"):t+n+e+"="+r}static convertFormToObject(t){const e={};return t.querySelectorAll("input, select, textarea").forEach((t=>{const r=t.name,i=t.value;r&&(t instanceof HTMLInputElement&&"checkbox"==t.type?(void 0===e[r]&&(e[r]=[]),t.checked&&e[r].push(i)):e[r]=i)})),e}static mergeDeep(...t){const e=t=>t&&"object"==typeof t;return t.reduce(((t,r)=>(Object.keys(r).forEach((i=>{const n=t[i],a=r[i];Array.isArray(n)&&Array.isArray(a)?t[i]=n.concat(...a):e(n)&&e(a)?t[i]=Utility.mergeDeep(n,a):t[i]=a})),t)),{})}static urlsPointToSameServerSideResource(t,e){if(!t||!e)return!1;const r=window.location.origin;try{const i=new URL(t,Utility.isValidUrl(t)?void 0:r),n=new URL(e,Utility.isValidUrl(e)?void 0:r),a=i.origin+i.pathname+i.search;return a===n.origin+n.pathname+n.search}catch(t){return!1}}static isValidUrl(t){try{return new URL(t),!0}catch(t){return!1}}}export default Utility;
\ No newline at end of file
+class Utility{static trimExplode(t,e){return e.split(t).map((t=>t.trim())).filter((t=>""!==t))}static trimItems(t){return t.map((t=>t instanceof String?t.trim():t))}static intExplode(t,e,r=!1){return e.split(t).map((t=>parseInt(t,10))).filter((t=>!isNaN(t)||r&&0===t))}static isNumber(t){return!isNaN(parseFloat(t.toString()))&&isFinite(t)}static getParameterFromUrl(t,e){if(console.warn("The function `getParameterFromUrl()` of `@typo3/backend/utility` has been marked as deprecated and will be removed in TYPO3 v13. Use `new URL(url, window.location.origin).searchParams.get(parameter)` instead."),"function"!=typeof t.split)return"";const r=t.split("?");let i="";if(r.length>=2){const t=r.join("?"),a=encodeURIComponent(e)+"=",n=t.split(/[&;]/g);for(let t=n.length;t-- >0;)if(-1!==n[t].lastIndexOf(a,0)){i=n[t].split("=")[1];break}}return i}static updateQueryStringParameter(t,e,r){const i=new RegExp("([?&])"+e+"=.*?(&|$)","i"),a=t.includes("?")?"&":"?";return t.match(i)?t.replace(i,"$1"+e+"="+r+"$2"):t+a+e+"="+r}static convertFormToObject(t){const e={};return t.querySelectorAll("input, select, textarea").forEach((t=>{const r=t.name,i=t.value;if(r)if("input"===t.tagName.toLowerCase()&&"checkbox"==t.type){const a=t;void 0===e[r]&&(e[r]=[]),a.checked&&e[r].push(i)}else e[r]=i})),e}static mergeDeep(...t){const e=t=>t&&"object"==typeof t;return t.reduce(((t,r)=>(Object.keys(r).forEach((i=>{const a=t[i],n=r[i];Array.isArray(a)&&Array.isArray(n)?t[i]=a.concat(...n):e(a)&&e(n)?t[i]=Utility.mergeDeep(a,n):t[i]=n})),t)),{})}static urlsPointToSameServerSideResource(t,e){if(!t||!e)return!1;const r=window.location.origin;try{const i=new URL(t,Utility.isValidUrl(t)?void 0:r),a=new URL(e,Utility.isValidUrl(e)?void 0:r),n=i.origin+i.pathname+i.search;return n===a.origin+a.pathname+a.search}catch(t){return!1}}static isValidUrl(t){try{return new URL(t),!0}catch(t){return!1}}}export default Utility;
\ No newline at end of file
-- 
GitLab