diff --git a/js/src/ajax.js b/js/src/ajax.js
index 61b6c8f0d7..f1a123830f 100644
--- a/js/src/ajax.js
+++ b/js/src/ajax.js
@@ -895,6 +895,23 @@ $(document).on('ajaxError', function (event, request) {
var details = '';
var state = request.state();
+ if (
+ 'responseJSON' in request &&
+ 'isErrorResponse' in request.responseJSON &&
+ request.responseJSON.isErrorResponse
+ ) {
+ Functions.ajaxShowMessage(
+ '
' +
+ Functions.escapeHtml(request.responseJSON.error) +
+ '
',
+ false
+ );
+ AJAX.active = false;
+ AJAX.xhr = null;
+
+ return;
+ }
+
if (request.status !== 0) {
details += '' + Functions.escapeHtml(Functions.sprintf(Messages.strErrorCode, request.status)) + '
';
}
diff --git a/libraries/classes/Controllers/AbstractController.php b/libraries/classes/Controllers/AbstractController.php
index 158c735ef4..50d8ed644a 100644
--- a/libraries/classes/Controllers/AbstractController.php
+++ b/libraries/classes/Controllers/AbstractController.php
@@ -146,4 +146,22 @@ abstract class AbstractController
$this->response->setHttpResponseCode(400);
Core::fatalError($errorMessage);
}
+
+ /**
+ * @psalm-param int<400,599> $statusCode
+ */
+ protected function sendErrorResponse(string $message, int $statusCode = 400): void
+ {
+ $this->response->setHttpResponseCode($statusCode);
+ $this->response->setRequestStatus(false);
+
+ if ($this->response->isAjax()) {
+ $this->response->addJSON('isErrorResponse', true);
+ $this->response->addJSON('message', $message);
+
+ return;
+ }
+
+ $this->response->addHTML(Message::error($message)->getDisplay());
+ }
}
diff --git a/libraries/classes/Controllers/Table/DropColumnConfirmationController.php b/libraries/classes/Controllers/Table/DropColumnConfirmationController.php
index d402d4b980..8d8f30a0f9 100644
--- a/libraries/classes/Controllers/Table/DropColumnConfirmationController.php
+++ b/libraries/classes/Controllers/Table/DropColumnConfirmationController.php
@@ -25,15 +25,11 @@ final class DropColumnConfirmationController extends AbstractController
$table = TableName::fromValue($request->getParsedBodyParam('table'));
Assert::allStringNotEmpty($fields);
} catch (InvalidIdentifierName $exception) {
- $this->response->setHttpResponseCode(400);
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', $exception->getMessage());
+ $this->sendErrorResponse($exception->getMessage());
return;
} catch (InvalidArgumentException $exception) {
- $this->response->setHttpResponseCode(400);
- $this->response->setRequestStatus(false);
- $this->response->addJSON('message', __('No column selected.'));
+ $this->sendErrorResponse(__('No column selected.'));
return;
}
diff --git a/test/classes/Controllers/AbstractControllerTest.php b/test/classes/Controllers/AbstractControllerTest.php
index b56b9822bb..daa1908266 100644
--- a/test/classes/Controllers/AbstractControllerTest.php
+++ b/test/classes/Controllers/AbstractControllerTest.php
@@ -6,6 +6,7 @@ namespace PhpMyAdmin\Tests\Controllers;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Html\MySQLDocumentation;
+use PhpMyAdmin\Message;
use PhpMyAdmin\Sanitize;
use PhpMyAdmin\Template;
use PhpMyAdmin\Tests\AbstractTestCase;
@@ -58,8 +59,7 @@ class AbstractControllerTest extends AbstractTestCase
$_REQUEST = [];
$response = new ResponseRenderer();
- $template = new Template();
- $controller = new class ($response, $template) extends AbstractController {
+ $controller = new class ($response, new Template()) extends AbstractController {
/**
* @psalm-param non-empty-list $params
*/
@@ -80,4 +80,56 @@ class AbstractControllerTest extends AbstractTestCase
$this->assertSame(200, $response->getHttpResponseCode());
}
+
+ public function testSendErrorResponseWithJson(): void
+ {
+ $response = new ResponseRenderer();
+ $response->setAjax(true);
+
+ $controller = new class ($response, new Template()) extends AbstractController {
+ /**
+ * @psalm-param int<400,599> $statusCode
+ */
+ public function testSendErrorResponse(string $message, int $statusCode = 400): void
+ {
+ parent::sendErrorResponse($message, $statusCode);
+ }
+ };
+
+ $controller->testSendErrorResponse('Error message.', 404);
+
+ $this->assertSame(404, $response->getHttpResponseCode());
+ $this->assertFalse($response->hasSuccessState());
+ $this->assertSame('', $response->getHTMLResult());
+ $this->assertSame([
+ 'isErrorResponse' => true,
+ 'message' => 'Error message.',
+ ], $response->getJSONResult());
+ }
+
+ public function testSendErrorResponseWithHtml(): void
+ {
+ $response = new ResponseRenderer();
+ $response->setAjax(false);
+
+ $controller = new class ($response, new Template()) extends AbstractController {
+ /**
+ * @psalm-param int<400,599> $statusCode
+ */
+ public function testSendErrorResponse(string $message, int $statusCode = 400): void
+ {
+ parent::sendErrorResponse($message, $statusCode);
+ }
+ };
+
+ $controller->testSendErrorResponse('Error message.', 404);
+
+ $this->assertSame(404, $response->getHttpResponseCode());
+ $this->assertFalse($response->hasSuccessState());
+ $this->assertSame(
+ Message::error('Error message.')->getDisplay(),
+ $response->getHTMLResult()
+ );
+ $this->assertSame([], $response->getJSONResult());
+ }
}
diff --git a/test/classes/Controllers/Table/DropColumnConfirmationControllerTest.php b/test/classes/Controllers/Table/DropColumnConfirmationControllerTest.php
index b9358433ea..f8aad39dfa 100644
--- a/test/classes/Controllers/Table/DropColumnConfirmationControllerTest.php
+++ b/test/classes/Controllers/Table/DropColumnConfirmationControllerTest.php
@@ -28,6 +28,8 @@ class DropColumnConfirmationControllerTest extends AbstractTestCase
$this->dummyDbi->addResult('SHOW TABLES LIKE \'test_table\';', [['test_table']]);
$response = new ResponseRenderer();
+ $response->setAjax(true);
+
$template = new Template();
$expected = $template->render('table/structure/drop_confirm', [
'db' => 'test_db',
@@ -53,11 +55,16 @@ class DropColumnConfirmationControllerTest extends AbstractTestCase
]);
$response = new ResponseRenderer();
+ $response->setAjax(true);
+
(new DropColumnConfirmationController($response, new Template()))($request);
$this->assertSame(400, $response->getHttpResponseCode());
$this->assertFalse($response->hasSuccessState());
- $this->assertSame(['message' => 'No column selected.'], $response->getJSONResult());
+ $this->assertSame([
+ 'isErrorResponse' => true,
+ 'message' => 'No column selected.'
+ ], $response->getJSONResult());
$this->assertSame('', $response->getHTMLResult());
}
@@ -71,11 +78,16 @@ class DropColumnConfirmationControllerTest extends AbstractTestCase
]);
$response = new ResponseRenderer();
+ $response->setAjax(true);
+
(new DropColumnConfirmationController($response, new Template()))($request);
$this->assertSame(400, $response->getHttpResponseCode());
$this->assertFalse($response->hasSuccessState());
- $this->assertSame(['message' => 'The database name must be a non-empty string.'], $response->getJSONResult());
+ $this->assertSame([
+ 'isErrorResponse' => true,
+ 'message' => 'The database name must be a non-empty string.'
+ ], $response->getJSONResult());
$this->assertSame('', $response->getHTMLResult());
}
}