Extract recent table handling from Header into a middleware

This removes the recent visited table handling from the Header class and
moves it to a new middleware.

This also removes a duplicated update via front-end.

Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
This commit is contained in:
Maurício Meneghini Fauth 2024-04-14 17:19:26 -03:00
parent a6c56e6e0e
commit 9b25d12e01
No known key found for this signature in database
GPG Key ID: 6A16FD38AFC89CC8
13 changed files with 112 additions and 108 deletions

View File

@ -27,7 +27,6 @@ use PhpMyAdmin\Controllers\Normalization;
use PhpMyAdmin\Controllers\Operations;
use PhpMyAdmin\Controllers\PhpInfoController;
use PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Controllers\RecentTablesListController;
use PhpMyAdmin\Controllers\SchemaExportController;
use PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\Sql;
@ -544,10 +543,6 @@ return [
'class' => PhpInfoController::class,
'arguments' => ['$response' => '@response'],
],
RecentTablesListController::class => [
'class' => RecentTablesListController::class,
'arguments' => ['$response' => '@response'],
],
Preferences\ExportController::class => [
'class' => Preferences\ExportController::class,
'arguments' => [

View File

@ -8217,7 +8217,7 @@ parameters:
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 8
count: 7
path: src/Header.php
-

View File

@ -2554,11 +2554,6 @@
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/RecentTablesListController.php">
<PossiblyUnusedMethod>
<code><![CDATA[__construct]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/Controllers/SchemaExportController.php">
<PossiblyUnusedReturnValue>
<code><![CDATA[Response|null]]></code>
@ -6533,7 +6528,6 @@
<RiskyTruthyFalsyComparison>
<code><![CDATA[! $this->config->get('DisableShortcutKeys')]]></code>
<code><![CDATA[empty($_REQUEST['message'])]]></code>
<code><![CDATA[empty($_REQUEST['recent_table'])]]></code>
</RiskyTruthyFalsyComparison>
</file>
<file src="src/Html/Generator.php">

View File

@ -3121,24 +3121,10 @@ function getPageSelectorEventHandler () {
}
function teardownRecentFavoriteTables (): void {
$('#update_recent_tables').off('ready');
$('#sync_favorite_tables').off('ready');
}
function onloadRecentFavoriteTables (): void {
var $updateRecentTables = $('#update_recent_tables');
if ($updateRecentTables.length) {
$.get(
$updateRecentTables.attr('href'),
{ 'no_debug': true },
function (data) {
if (typeof data !== 'undefined' && data.success === true) {
$('#recentTableList').html(data.list);
}
}
);
}
// Sync favorite tables from localStorage to pmadb.
if (! $('#sync_favorite_tables').length) {
return;

View File

@ -117,7 +117,6 @@
<div id="page_content">
{{ messages|raw }}
{{ recent_table|raw }}
{%- if is_logged_in -%}
{{ include('modals/preview_sql_modal.twig') }}
{{ include('modals/enum_set_editor.twig') }}

View File

@ -29,6 +29,7 @@ use PhpMyAdmin\Http\Middleware\OutputBuffering;
use PhpMyAdmin\Http\Middleware\PhpExtensionsChecking;
use PhpMyAdmin\Http\Middleware\PhpSettingsConfiguration;
use PhpMyAdmin\Http\Middleware\ProfilingChecking;
use PhpMyAdmin\Http\Middleware\RecentTableHandling;
use PhpMyAdmin\Http\Middleware\RequestProblemChecking;
use PhpMyAdmin\Http\Middleware\ResponseRendererLoading;
use PhpMyAdmin\Http\Middleware\RouteParsing;
@ -114,6 +115,7 @@ class Application
$requestHandler->add(new TokenMismatchChecking());
$requestHandler->add(new ProfilingChecking());
$requestHandler->add(new UserPreferencesLoading($this->config));
$requestHandler->add(new RecentTableHandling($this->config));
$runner = new RequestHandlerRunner(
$requestHandler,

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers;
use PhpMyAdmin\Favorites\RecentFavoriteTables;
use PhpMyAdmin\Favorites\TableType;
use PhpMyAdmin\Http\Response;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\ResponseRenderer;
final class RecentTablesListController implements InvocableController
{
public function __construct(private readonly ResponseRenderer $response)
{
}
public function __invoke(ServerRequest $request): Response|null
{
if (! $request->isAjax()) {
return null;
}
$this->response->addJSON(['list' => RecentFavoriteTables::getInstance(TableType::Recent)->getHtmlList()]);
return null;
}
}

View File

@ -329,16 +329,6 @@ class RecentFavoriteTables
return $retval;
}
/**
* Generate Html to update recent tables.
*/
public static function getHtmlUpdateRecentTables(): string
{
return '<a class="hide" id="update_recent_tables" href="'
. Url::getFromRoute('/recent-table', ['ajax_request' => true, 'recent_table' => true])
. '"></a>';
}
/**
* Return the name of the configuration storage table
*

View File

@ -9,12 +9,7 @@ namespace PhpMyAdmin;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Container\ContainerBuilder;
use PhpMyAdmin\Favorites\RecentFavoriteTable;
use PhpMyAdmin\Favorites\RecentFavoriteTables;
use PhpMyAdmin\Favorites\TableType;
use PhpMyAdmin\Html\Generator;
use PhpMyAdmin\Identifiers\DatabaseName;
use PhpMyAdmin\Identifiers\TableName;
use PhpMyAdmin\Navigation\Navigation;
use PhpMyAdmin\Theme\ThemeManager;
@ -252,22 +247,10 @@ class Header
*/
public function getDisplay(): string
{
if ($this->headerIsSent || ! $this->isEnabled) {
if ($this->headerIsSent || ! $this->isEnabled || $this->isAjax) {
return '';
}
$recentTable = '';
if (empty($_REQUEST['recent_table']) && Current::$table !== '') {
$recentTable = $this->addRecentTable(
DatabaseName::from(Current::$database),
TableName::from(Current::$table),
);
}
if ($this->isAjax) {
return $recentTable;
}
$this->sendHttpHeaders();
$baseDir = defined('PMA_PATH_TO_BASEDIR') ? PMA_PATH_TO_BASEDIR : '';
@ -359,7 +342,6 @@ class Header
'menu' => $menu ?? '',
'console' => $console,
'messages' => $messages,
'recent_table' => $recentTable,
'theme_color_mode' => $theme->getColorMode(),
'theme_color_modes' => $theme->getColorModes(),
'theme_id' => $theme->getId(),
@ -580,24 +562,6 @@ class Header
return $headers;
}
/**
* Add recently used table and reload the navigation.
*/
private function addRecentTable(DatabaseName $db, TableName $table): string
{
if ($this->menuEnabled && $this->config->settings['NumRecentTables'] > 0) {
$favoriteTable = new RecentFavoriteTable($db, $table);
$error = RecentFavoriteTables::getInstance(TableType::Recent)->add($favoriteTable);
if ($error === true) {
return RecentFavoriteTables::getHtmlUpdateRecentTables();
}
return $error->getDisplay();
}
return '';
}
/**
* Returns the phpMyAdmin version to be appended to the url to avoid caching
* between versions

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Http\Middleware;
use PhpMyAdmin\Config;
use PhpMyAdmin\Favorites\RecentFavoriteTable;
use PhpMyAdmin\Favorites\RecentFavoriteTables;
use PhpMyAdmin\Favorites\TableType;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
use PhpMyAdmin\Identifiers\TableName;
use PhpMyAdmin\Message;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
final class RecentTableHandling implements MiddlewareInterface
{
public function __construct(private readonly Config $config)
{
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
assert($request instanceof ServerRequest);
if ($this->config->settings['NumRecentTables'] === 0) {
return $handler->handle($request);
}
$response = $handler->handle($request);
$db = DatabaseName::tryFrom($request->getParam('db'));
$table = TableName::tryFrom($request->getParam('table'));
if ($db !== null && $table !== null) {
$recentTable = new RecentFavoriteTable($db, $table);
$isAddedOrError = RecentFavoriteTables::getInstance(TableType::Recent)->add($recentTable);
if ($isAddedOrError instanceof Message) {
$response->getBody()->write($isAddedOrError->getMessage());
}
}
return $response;
}
}

View File

@ -30,7 +30,6 @@ use PhpMyAdmin\Controllers\Normalization;
use PhpMyAdmin\Controllers\Operations;
use PhpMyAdmin\Controllers\PhpInfoController;
use PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Controllers\RecentTablesListController;
use PhpMyAdmin\Controllers\SchemaExportController;
use PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\Sql;
@ -172,7 +171,6 @@ final class Routes
$routes->addRoute(['GET', 'POST'], '/sql', Preferences\SqlController::class);
$routes->addRoute(['GET', 'POST'], '/two-factor', Preferences\TwoFactorController::class);
});
$routes->addRoute(['GET', 'POST'], '/recent-table', RecentTablesListController::class);
$routes->addRoute(['GET', 'POST'], '/schema-export', SchemaExportController::class);
$routes->addGroup('/server', static function (RouteCollector $routes): void {
$routes->addRoute(['GET', 'POST'], '/binlog', Server\BinlogController::class);

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Http\Middleware;
use PhpMyAdmin\Config;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\Favorites\RecentFavoriteTable;
use PhpMyAdmin\Favorites\RecentFavoriteTables;
use PhpMyAdmin\Favorites\TableType;
use PhpMyAdmin\Http\Factory\ResponseFactory;
use PhpMyAdmin\Http\Factory\ServerRequestFactory;
use PhpMyAdmin\Http\Middleware\RecentTableHandling;
use PhpMyAdmin\Identifiers\DatabaseName;
use PhpMyAdmin\Identifiers\TableName;
use PhpMyAdmin\Tests\AbstractTestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use Psr\Http\Server\RequestHandlerInterface;
use ReflectionProperty;
#[CoversClass(RecentTableHandling::class)]
final class RecentTableHandlingTest extends AbstractTestCase
{
public function testProcess(): void
{
$dbiDummy = $this->createDbiDummy();
DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy);
$dbiDummy->addResult('SELECT 1 FROM `test_db`.`test_table` LIMIT 1;', [['1']]);
$config = new Config();
$config->settings['NumRecentTables'] = 10;
$request = ServerRequestFactory::create()->createServerRequest('GET', 'https://example.com/')
->withQueryParams(['db' => 'test_db', 'table' => 'test_table']);
$response = ResponseFactory::create()->createResponse();
$handler = self::createMock(RequestHandlerInterface::class);
$handler->method('handle')->with($request)->willReturn($response);
$reflectionProperty = new ReflectionProperty(RecentFavoriteTables::class, 'instances');
$reflectionProperty->setValue(null, []);
$recentTables = RecentFavoriteTables::getInstance(TableType::Recent);
self::assertSame([], $recentTables->getTables());
$actualResponse = (new RecentTableHandling($config))->process($request, $handler);
self::assertSame($response, $actualResponse);
self::assertSame('', (string) $actualResponse->getBody());
self::assertEquals(
[new RecentFavoriteTable(DatabaseName::from('test_db'), TableName::from('test_table'))],
$recentTables->getTables(),
);
$dbiDummy->assertAllQueriesConsumed();
$reflectionProperty->setValue(null, []);
}
}

View File

@ -33,7 +33,6 @@ use PhpMyAdmin\Controllers\Normalization;
use PhpMyAdmin\Controllers\Operations;
use PhpMyAdmin\Controllers\PhpInfoController;
use PhpMyAdmin\Controllers\Preferences;
use PhpMyAdmin\Controllers\RecentTablesListController;
use PhpMyAdmin\Controllers\SchemaExportController;
use PhpMyAdmin\Controllers\Server;
use PhpMyAdmin\Controllers\Sql;
@ -100,7 +99,6 @@ final class RoutesTest extends TestCase
'/preferences/navigation' => Preferences\NavigationController::class,
'/preferences/sql' => Preferences\SqlController::class,
'/preferences/two-factor' => Preferences\TwoFactorController::class,
'/recent-table' => RecentTablesListController::class,
'/schema-export' => SchemaExportController::class,
'/server/binlog' => Server\BinlogController::class,
'/server/collations' => Server\CollationsController::class,
@ -238,7 +236,6 @@ final class RoutesTest extends TestCase
'/preferences/navigation' => Preferences\NavigationController::class,
'/preferences/sql' => Preferences\SqlController::class,
'/preferences/two-factor' => Preferences\TwoFactorController::class,
'/recent-table' => RecentTablesListController::class,
'/schema-export' => SchemaExportController::class,
'/server/binlog' => Server\BinlogController::class,
'/server/databases' => Server\DatabasesController::class,