From eee662afe979eebd18e6877859f2017becd6c80a Mon Sep 17 00:00:00 2001
From: Andreas Kienast <a.fernandez@scripting-base.de>
Date: Mon, 26 Feb 2024 08:24:45 +0100
Subject: [PATCH] [BUGFIX] Do not race tree node loading

When (de-)mounting pages in the page tree, always two requests are sent
to the server that fetch the tree data:

* when the `mountPointPath` path property is changed as it's a state
  property
* explicitly after changing said property

Especially when de-mounting a page, this tends to race conditions, as
the mount state is additionally stored in the backend user's UC that
needs to get unset as well.

This bugfix now changes the handling when the state property, that
triggers a re-rendering of the tree component, is updated. The
de-mounting process now unsets the backend user's UC first and then
changes the property to make sure that

1. the state is updated once the configuration is flushed
2. the tree nodes are fetched only once

In the mounting process, the UC is correctly updated first. However, the
state was updated and then the nodes were reloaded explicitly. The
latter is not required at all.

Resolves: #103185
Releases: main, 12.4
Change-Id: I7a06852c6af40e1a41691a7cc7acd41db4f84adc
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/83126
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Benjamin Kott <benjamin.kott@outlook.com>
Reviewed-by: Andreas Kienast <a.fernandez@scripting-base.de>
Reviewed-by: Garvin Hicking <gh@faktor-e.de>
Tested-by: Andreas Kienast <a.fernandez@scripting-base.de>
Tested-by: Benjamin Kott <benjamin.kott@outlook.com>
Tested-by: Garvin Hicking <gh@faktor-e.de>
---
 Build/Sources/TypeScript/backend/tree/page-browser.ts         | 4 +---
 Build/Sources/TypeScript/backend/tree/page-tree-element.ts    | 4 +---
 .../backend/Resources/Public/JavaScript/tree/page-browser.js  | 4 ++--
 .../Resources/Public/JavaScript/tree/page-tree-element.js     | 4 ++--
 4 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/tree/page-browser.ts b/Build/Sources/TypeScript/backend/tree/page-browser.ts
index c741190af0e9..f2bebb8b13a0 100644
--- a/Build/Sources/TypeScript/backend/tree/page-browser.ts
+++ b/Build/Sources/TypeScript/backend/tree/page-browser.ts
@@ -219,9 +219,8 @@ export class PageBrowser extends LitElement {
   };
 
   private unsetTemporaryMountPoint() {
-    this.mountPointPath = null;
     Persistent.unset('pageTree_temporaryMountPoint').then(() => {
-      this.tree.loadData();
+      this.mountPointPath = null;
     });
   }
 
@@ -252,7 +251,6 @@ export class PageBrowser extends LitElement {
           this.tree.loadData();
         } else {
           this.mountPointPath = response.mountPointPath;
-          this.tree.refreshOrFilterTree();
         }
       })
       .catch((error) => {
diff --git a/Build/Sources/TypeScript/backend/tree/page-tree-element.ts b/Build/Sources/TypeScript/backend/tree/page-tree-element.ts
index a1257d83ecba..57c16d5df624 100644
--- a/Build/Sources/TypeScript/backend/tree/page-tree-element.ts
+++ b/Build/Sources/TypeScript/backend/tree/page-tree-element.ts
@@ -386,9 +386,8 @@ export class PageTreeNavigationComponent extends LitElement {
   };
 
   private unsetTemporaryMountPoint() {
-    this.mountPointPath = null;
     Persistent.unset('pageTree_temporaryMountPoint').then(() => {
-      this.tree.loadData();
+      this.mountPointPath = null;
     });
   }
 
@@ -419,7 +418,6 @@ export class PageTreeNavigationComponent extends LitElement {
           this.tree.loadData();
         } else {
           this.mountPointPath = response.mountPointPath;
-          this.tree.refreshOrFilterTree();
         }
       })
       .catch((error) => {
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-browser.js b/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-browser.js
index 11ce629c6735..c872de60de91 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-browser.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-browser.js
@@ -28,7 +28,7 @@ var __decorate=function(e,t,n,o){var r,i=arguments.length,s=i<3?t:null===o?o=Obj
             ${this.renderMountPoint()}
             <typo3-backend-component-page-browser-tree id="typo3-pagetree-tree" class="tree-wrapper" .setup=${e} @tree:initialized=${()=>{this.tree.addEventListener("typo3:tree:node-selected",this.loadRecordsOfPage),this.tree.addEventListener("typo3:tree:nodes-prepared",this.selectActivePageInTree);this.querySelector("typo3-backend-tree-toolbar").tree=this.tree}}></typo3-backend-component-page-browser-tree>
           </div>
-        `))}unsetTemporaryMountPoint(){this.mountPointPath=null,Persistent.unset("pageTree_temporaryMountPoint").then((()=>{this.tree.loadData()}))}renderMountPoint(){return null===this.mountPointPath?nothing:html`
+        `))}unsetTemporaryMountPoint(){Persistent.unset("pageTree_temporaryMountPoint").then((()=>{this.mountPointPath=null}))}renderMountPoint(){return null===this.mountPointPath?nothing:html`
       <div class="node-mount-point">
         <div class="node-mount-point__icon"><typo3-backend-icon identifier="actions-info-circle" size="small"></typo3-backend-icon></div>
         <div class="node-mount-point__text">${this.mountPointPath}</div>
@@ -36,4 +36,4 @@ var __decorate=function(e,t,n,o){var r,i=arguments.length,s=i<3?t:null===o?o=Obj
           <typo3-backend-icon identifier="actions-close" size="small"></typo3-backend-icon>
         </div>
       </div>
-    `}setTemporaryMountPoint(e){new AjaxRequest(this.configuration.setTemporaryMountPointUrl).post("pid="+e,{headers:{"Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"}}).then((e=>e.resolve())).then((e=>{e&&e.hasErrors?(this.tree.errorNotification(e.message),this.tree.loadData()):(this.mountPointPath=e.mountPointPath,this.tree.refreshOrFilterTree())})).catch((e=>{this.tree.errorNotification(e),this.tree.loadData()}))}};__decorate([property({type:String})],PageBrowser.prototype,"mountPointPath",void 0),__decorate([query(".tree-wrapper")],PageBrowser.prototype,"tree",void 0),PageBrowser=__decorate([customElement("typo3-backend-component-page-browser")],PageBrowser);export{PageBrowser};
\ No newline at end of file
+    `}setTemporaryMountPoint(e){new AjaxRequest(this.configuration.setTemporaryMountPointUrl).post("pid="+e,{headers:{"Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"}}).then((e=>e.resolve())).then((e=>{e&&e.hasErrors?(this.tree.errorNotification(e.message),this.tree.loadData()):this.mountPointPath=e.mountPointPath})).catch((e=>{this.tree.errorNotification(e),this.tree.loadData()}))}};__decorate([property({type:String})],PageBrowser.prototype,"mountPointPath",void 0),__decorate([query(".tree-wrapper")],PageBrowser.prototype,"tree",void 0),PageBrowser=__decorate([customElement("typo3-backend-component-page-browser")],PageBrowser);export{PageBrowser};
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-tree-element.js b/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-tree-element.js
index e428485036af..f399fbf820c8 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-tree-element.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/tree/page-tree-element.js
@@ -20,7 +20,7 @@ var __decorate=function(e,t,o,n){var a,r=arguments.length,i=r<3?t:null===n?n=Obj
             ${this.renderMountPoint()}
             <typo3-backend-navigation-component-pagetree-tree id="typo3-pagetree-tree" class="tree-wrapper" .setup=${e} @tree:initialized=${()=>{this.toolbar.tree=this.tree,this.tree.addEventListener("typo3:tree:node-selected",this.loadContent),this.tree.addEventListener("typo3:tree:node-context",this.showContextMenu),this.tree.addEventListener("typo3:tree:nodes-prepared",this.selectActiveNode)}}></typo3-backend-navigation-component-pagetree-tree>
           </div>
-        `))}unsetTemporaryMountPoint(){this.mountPointPath=null,Persistent.unset("pageTree_temporaryMountPoint").then((()=>{this.tree.loadData()}))}renderMountPoint(){return null===this.mountPointPath?nothing:html`
+        `))}unsetTemporaryMountPoint(){Persistent.unset("pageTree_temporaryMountPoint").then((()=>{this.mountPointPath=null}))}renderMountPoint(){return null===this.mountPointPath?nothing:html`
       <div class="node-mount-point">
         <div class="node-mount-point__icon"><typo3-backend-icon identifier="actions-info-circle" size="small"></typo3-backend-icon></div>
         <div class="node-mount-point__text">${this.mountPointPath}</div>
@@ -28,7 +28,7 @@ var __decorate=function(e,t,o,n){var a,r=arguments.length,i=r<3?t:null===n?n=Obj
           <typo3-backend-icon identifier="actions-close" size="small"></typo3-backend-icon>
         </div>
       </div>
-    `}setTemporaryMountPoint(e){new AjaxRequest(this.configuration.setTemporaryMountPointUrl).post("pid="+e,{headers:{"Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"}}).then((e=>e.resolve())).then((e=>{e&&e.hasErrors?(this.tree.errorNotification(e.message),this.tree.loadData()):(this.mountPointPath=e.mountPointPath,this.tree.refreshOrFilterTree())})).catch((e=>{this.tree.errorNotification(e),this.tree.loadData()}))}};__decorate([property({type:String})],PageTreeNavigationComponent.prototype,"mountPointPath",void 0),__decorate([query(".tree-wrapper")],PageTreeNavigationComponent.prototype,"tree",void 0),__decorate([query("typo3-backend-navigation-component-pagetree-toolbar")],PageTreeNavigationComponent.prototype,"toolbar",void 0),PageTreeNavigationComponent=__decorate([customElement("typo3-backend-navigation-component-pagetree")],PageTreeNavigationComponent);export{PageTreeNavigationComponent};let PageTreeToolbar=class extends TreeToolbar{constructor(){super(...arguments),this.tree=null}render(){return html`
+    `}setTemporaryMountPoint(e){new AjaxRequest(this.configuration.setTemporaryMountPointUrl).post("pid="+e,{headers:{"Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"}}).then((e=>e.resolve())).then((e=>{e&&e.hasErrors?(this.tree.errorNotification(e.message),this.tree.loadData()):this.mountPointPath=e.mountPointPath})).catch((e=>{this.tree.errorNotification(e),this.tree.loadData()}))}};__decorate([property({type:String})],PageTreeNavigationComponent.prototype,"mountPointPath",void 0),__decorate([query(".tree-wrapper")],PageTreeNavigationComponent.prototype,"tree",void 0),__decorate([query("typo3-backend-navigation-component-pagetree-toolbar")],PageTreeNavigationComponent.prototype,"toolbar",void 0),PageTreeNavigationComponent=__decorate([customElement("typo3-backend-navigation-component-pagetree")],PageTreeNavigationComponent);export{PageTreeNavigationComponent};let PageTreeToolbar=class extends TreeToolbar{constructor(){super(...arguments),this.tree=null}render(){return html`
       <div class="tree-toolbar">
         <div class="tree-toolbar__menu">
           <div class="tree-toolbar__search">
-- 
GitLab