Skip to content

Commit d0221e7

Browse files
authored
[BUGFIX] handle DataHandler save errors without breaking the editor (#68)
Return DataHandler error messages from the persistence middleware and show them as backend notifications in the frontend save flow. This keeps known save failures visible to the editor user and reloads the page afterwards so the UI can recover from a broken save state.
1 parent 1dae124 commit d0221e7

8 files changed

Lines changed: 59 additions & 16 deletions

File tree

Classes/Middleware/PersistenceMiddleware.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,14 @@ private function saveStuff(ServerRequestInterface $request): ResponseInterface
7878
throw new RuntimeException('Unknown data operations: ' . implode(', ', array_keys($input)) . ' only data and cmdArray are allowed', 8110225095);
7979
}
8080

81-
$this->dataHandlerService->run($data, []);
81+
$errorLog = $this->dataHandlerService->run($data, []);
8282

8383
foreach ($cmdArray as $cmd) {
84-
$this->dataHandlerService->run([], $cmd);
84+
$errorLog = [...$errorLog, ...$this->dataHandlerService->run([], $cmd)];
85+
}
86+
87+
if ($errorLog) {
88+
return new JsonResponse(['success' => false, 'errorLog' => $errorLog], 500);
8589
}
8690

8791
return new JsonResponse(['success' => true]);

Classes/Service/DataHandlerService.php

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
1010
use TYPO3\CMS\Core\Utility\GeneralUtility;
1111

12-
use function count;
13-
use function implode;
1412
use function in_array;
1513
use function is_array;
1614
use function is_int;
@@ -25,8 +23,10 @@ public function __construct(
2523
/**
2624
* @param array<string, array<int, array<string, bool|int|float|string>>> $data
2725
* @param array<string, array<int, array<string, mixed>>> $cmd
26+
*
27+
* @return list<string>
2828
*/
29-
public function run(array $data, array $cmd): void
29+
public function run(array $data, array $cmd): array
3030
{
3131
$this->validateData($data);
3232
$this->validateCmd($cmd);
@@ -35,11 +35,7 @@ public function run(array $data, array $cmd): void
3535
$dataHandler->start($data, $cmd);
3636
$dataHandler->process_datamap();
3737
$dataHandler->process_cmdmap();
38-
if (count($dataHandler->errorLog) > 0) {
39-
throw new RuntimeException('Error running DataHandler: ' . implode(', ', $dataHandler->errorLog), 4718411495);
40-
}
41-
42-
unset($dataHandler);
38+
return $dataHandler->errorLog;
4339
}
4440

4541
/**

Resources/Private/Language/de.locallang.xlf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
<trans-unit id="save.changes">
2424
<target>%d Änderungen speichern</target>
2525
</trans-unit>
26+
<trans-unit id="save.failed">
27+
<target>Fehler beim Speichern</target>
28+
</trans-unit>
2629
<trans-unit id="save.fixInvalidField">
2730
<target>1 Fehler beheben</target>
2831
</trans-unit>

Resources/Private/Language/locallang.xlf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
<trans-unit id="save.changes">
2424
<source>Save %d changes</source>
2525
</trans-unit>
26+
<trans-unit id="save.failed">
27+
<source>Error while saving</source>
28+
</trans-unit>
2629
<trans-unit id="save.fixInvalidField">
2730
<source>Fix an error</source>
2831
</trans-unit>

Resources/Public/JavaScript/Frontend/initialize-save-handling.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ export async function trySave() {
3333

3434
try {
3535
const updatePageTree = dataHandlerStore.hasChangesIn('pages');
36-
await useDataHandler(dataHandlerStore.data, dataHandlerStore.cmdArray);
36+
const saveOk = await useDataHandler(dataHandlerStore.data, dataHandlerStore.cmdArray);
3737
dataHandlerStore.markSaved();
3838
sendMessage('saveEnded', {updatePageTree});
39+
if (!saveOk) {
40+
window.location.reload();
41+
}
3942
} finally {
4043
saving = false;
4144
}
Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import Notification from '@typo3/backend/notification.js';
2+
import {lll} from "@typo3/core/lit-helper.js";
3+
14
/**
25
* @typedef {Record<string, Record<number, Record<string, boolean|number|string>>>} Data
36
* @typedef {Record<string, Record<number, Record<'move'|'copy'|'delete', any>>>} Cmd
47
* @param {Data} data
58
* @param {Cmd[]} cmdArray
6-
* @returns {Promise<void>}
9+
* @returns {Promise<boolean>} returns false if something broke
710
*/
811
export async function useDataHandler(data = {}, cmdArray = []) {
912
const response = await fetch(window.location.href, {
@@ -14,9 +17,28 @@ export async function useDataHandler(data = {}, cmdArray = []) {
1417
},
1518
body: JSON.stringify({data, cmdArray}, null, 2),
1619
});
17-
if (!response.ok) {
18-
// TODO handle innerHTML differently, (maybe return json exception with message and details instead of whole HTML)
19-
document.body.innerHTML = await response.text();
20-
throw new Error('Failed to save data');
20+
21+
if (response.ok) {
22+
return true;
2123
}
24+
25+
let body = await response.text();
26+
27+
// if response has json parse it and check if errorLog is included:
28+
if (response.headers.get('Content-Type')?.includes('application/json')) {
29+
const data = JSON.parse(body);
30+
if (data.errorLog) {
31+
for (const error of data.errorLog) {
32+
Notification.error(lll('save.failed'), error);
33+
}
34+
return false;
35+
}
36+
const pre = document.createElement("PRE");
37+
pre.innerText = JSON.stringify(data, null, 2);
38+
body = pre.outerHTML;
39+
}
40+
41+
// TODO handle innerHTML differently, (maybe return json exception with message and details instead of whole HTML)
42+
document.body.innerHTML = body;
43+
throw new Error('Failed to save data');
2244
}

phpstan-baseline-13.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,9 @@ parameters:
149149
identifier: method.notFound
150150
count: 4
151151
path: Classes/PhpStanRule/NamedArgumentUsageRule.php
152+
153+
-
154+
message: '#^Method TYPO3\\CMS\\VisualEditor\\Service\\DataHandlerService\:\:run\(\) should return list\<string\> but returns array\.$#'
155+
identifier: return.type
156+
count: 1
157+
path: Classes/Service/DataHandlerService.php

phpstan-baseline-14.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ parameters:
1818
count: 4
1919
path: Classes/PhpStanRule/NamedArgumentUsageRule.php
2020

21+
-
22+
message: '#^Method TYPO3\\CMS\\VisualEditor\\Service\\DataHandlerService\:\:run\(\) should return list\<string\> but returns array\.$#'
23+
identifier: return.type
24+
count: 1
25+
path: Classes/Service/DataHandlerService.php
26+
2127
-
2228
message: '#^Cannot access offset ''allowed\.'' on TYPO3\\CMS\\Core\\Page\\ContentArea\.$#'
2329
identifier: offsetAccess.nonOffsetAccessible

0 commit comments

Comments
 (0)