From 5f4fe6c50d7b9af576ae47c7cd1531ad7c93e985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Sat, 20 Mar 2021 15:40:07 -0300 Subject: [PATCH 1/3] Add a flash messages service provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Used to persist messages from the current request to the next request. Signed-off-by: Maurício Meneghini Fauth --- libraries/classes/FlashMessages.php | 54 +++++++++++++++++++++++++ test/classes/FlashMessagesTest.php | 63 +++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 libraries/classes/FlashMessages.php create mode 100644 test/classes/FlashMessagesTest.php diff --git a/libraries/classes/FlashMessages.php b/libraries/classes/FlashMessages.php new file mode 100644 index 0000000000..40b5da34a4 --- /dev/null +++ b/libraries/classes/FlashMessages.php @@ -0,0 +1,54 @@ + */ + private $previousMessages = []; + + public function __construct() + { + if (! isset($_SESSION)) { + throw new RuntimeException(__('Session not found.')); + } + + $this->storage = &$_SESSION; + + if (isset($this->storage[self::STORAGE_KEY])) { + $this->previousMessages = $this->storage[self::STORAGE_KEY]; + } + + $this->storage[self::STORAGE_KEY] = []; + } + + public function addMessage(string $key, string $message): void + { + if (! isset($this->storage[self::STORAGE_KEY][$key])) { + $this->storage[self::STORAGE_KEY][$key] = []; + } + + $this->storage[self::STORAGE_KEY][$key][] = $message; + } + + /** @return string[]|null */ + public function getMessage(string $key): ?array + { + return $this->previousMessages[$key] ?? null; + } + + /** @return array */ + public function getMessages(): array + { + return $this->previousMessages; + } +} diff --git a/test/classes/FlashMessagesTest.php b/test/classes/FlashMessagesTest.php new file mode 100644 index 0000000000..bc60eab825 --- /dev/null +++ b/test/classes/FlashMessagesTest.php @@ -0,0 +1,63 @@ +assertArrayNotHasKey(self::STORAGE_KEY, $_SESSION); + $flash = new FlashMessages(); + $this->assertIsArray($_SESSION[self::STORAGE_KEY]); + + $_SESSION = null; + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Session not found.'); + $flash = new FlashMessages(); + } + + public function testAddMessage(): void + { + $flash = new FlashMessages(); + $this->assertArrayNotHasKey('error', $_SESSION[self::STORAGE_KEY]); + $flash->addMessage('error', 'Error'); + $this->assertArrayHasKey('error', $_SESSION[self::STORAGE_KEY]); + $this->assertIsArray($_SESSION[self::STORAGE_KEY]['error']); + $this->assertEquals(['Error'], $_SESSION[self::STORAGE_KEY]['error']); + } + + public function testGetMessage(): void + { + $_SESSION[self::STORAGE_KEY] = ['warning' => ['Warning']]; + $flash = new FlashMessages(); + $message = $flash->getMessage('error'); + $this->assertNull($message); + $message = $flash->getMessage('warning'); + $this->assertEquals(['Warning'], $message); + } + + public function testGetMessages(): void + { + $_SESSION[self::STORAGE_KEY] = [ + 'error' => ['Error1', 'Error2'], + 'warning' => ['Warning'], + ]; + $flash = new FlashMessages(); + $flash->addMessage('notice', 'Notice'); + $messages = $flash->getMessages(); + $this->assertEquals( + [ + 'error' => ['Error1', 'Error2'], + 'warning' => ['Warning'], + ], + $messages + ); + } +} From 7e23436533ed317d5f6893dbf776554c73f95e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Sat, 20 Mar 2021 18:51:36 -0300 Subject: [PATCH 2/3] Add Twig\FlashMessagesExtension class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the flash() Twig function that return all flash messages. Signed-off-by: Maurício Meneghini Fauth --- libraries/classes/Template.php | 7 ++++++- .../classes/Twig/FlashMessagesExtension.php | 18 ++++++++++++++++++ libraries/services.php | 6 +++++- scripts/console | 2 ++ test/bootstrap-dist.php | 3 +++ test/classes/TemplateTest.php | 2 ++ 6 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 libraries/classes/Twig/FlashMessagesExtension.php diff --git a/libraries/classes/Template.php b/libraries/classes/Template.php index 5ce3aae5b3..34d4bb6d22 100644 --- a/libraries/classes/Template.php +++ b/libraries/classes/Template.php @@ -7,6 +7,7 @@ namespace PhpMyAdmin; use PhpMyAdmin\Twig\AssetExtension; use PhpMyAdmin\Twig\CoreExtension; use PhpMyAdmin\Twig\Extensions\Node\TransNode; +use PhpMyAdmin\Twig\FlashMessagesExtension; use PhpMyAdmin\Twig\I18nExtension; use PhpMyAdmin\Twig\MessageExtension; use PhpMyAdmin\Twig\PluginsExtension; @@ -25,6 +26,7 @@ use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Twig\Extension\DebugExtension; use Twig\Loader\FilesystemLoader; +use Twig\RuntimeLoader\ContainerRuntimeLoader; use Twig\TemplateWrapper; use function sprintf; @@ -61,7 +63,7 @@ class Template public static function getTwigEnvironment(?string $cacheDir): Environment { - global $cfg; + global $cfg, $containerBuilder; /* Twig expects false when cache is not configured */ if ($cacheDir === null) { @@ -74,6 +76,8 @@ class Template 'cache' => $cacheDir, ]); + $twig->addRuntimeLoader(new ContainerRuntimeLoader($containerBuilder)); + if ($cfg['environment'] === 'development') { $twig->enableDebug(); $twig->addExtension(new DebugExtension()); @@ -89,6 +93,7 @@ class Template $twig->addExtension(new AssetExtension()); $twig->addExtension(new CoreExtension()); + $twig->addExtension(new FlashMessagesExtension()); $twig->addExtension(new I18nExtension()); $twig->addExtension(new MessageExtension()); $twig->addExtension(new PluginsExtension()); diff --git a/libraries/classes/Twig/FlashMessagesExtension.php b/libraries/classes/Twig/FlashMessagesExtension.php new file mode 100644 index 0000000000..f94c5a3e8f --- /dev/null +++ b/libraries/classes/Twig/FlashMessagesExtension.php @@ -0,0 +1,18 @@ + [ 'class' => Symfony\Component\ExpressionLanguage\ExpressionLanguage::class, ], + 'flash' => [ + 'class' => PhpMyAdmin\FlashMessages::class, + ], 'http_request' => [ 'class' => PhpMyAdmin\Utils\HttpRequest::class, ], @@ -214,7 +217,8 @@ return [ 'user_preferences' => [ 'class' => PhpMyAdmin\UserPreferences::class, ], - PhpMyAdmin\Response::class => 'response', PhpMyAdmin\DatabaseInterface::class => 'dbi', + PhpMyAdmin\FlashMessages::class => 'flash', + PhpMyAdmin\Response::class => 'response', ], ]; diff --git a/scripts/console b/scripts/console index 239cc37fbc..ee88c595ed 100755 --- a/scripts/console +++ b/scripts/console @@ -4,6 +4,7 @@ use PhpMyAdmin\Command\CacheWarmupCommand; use PhpMyAdmin\Command\SetVersionCommand; use PhpMyAdmin\Config; +use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\Template; use PhpMyAdmin\Tests\Stubs\DbiDummy; @@ -24,6 +25,7 @@ if (! class_exists(Application::class)) { exit(1); } +$containerBuilder = Core::getContainerBuilder(); $cfg['environment'] = 'production'; $config = new Config(CONFIG_FILE); $config->set('environment', $cfg['environment']); diff --git a/test/bootstrap-dist.php b/test/bootstrap-dist.php index 196719f2f9..d5125b2d76 100644 --- a/test/bootstrap-dist.php +++ b/test/bootstrap-dist.php @@ -6,6 +6,7 @@ declare(strict_types=1); use PhpMyAdmin\Config; +use PhpMyAdmin\Core; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\MoTranslator\Loader; use PhpMyAdmin\Tests\Stubs\DbiDummy; @@ -47,6 +48,8 @@ require_once ROOT_PATH . 'libraries/vendor_config.php'; require_once AUTOLOAD_FILE; Loader::loadFunctions(); +$GLOBALS['containerBuilder'] = Core::getContainerBuilder(); + $GLOBALS['config'] = new Config(); $GLOBALS['config']->set('environment', 'development'); $GLOBALS['cfg']['environment'] = 'development'; diff --git a/test/classes/TemplateTest.php b/test/classes/TemplateTest.php index dc4ce682e9..2db2e77855 100644 --- a/test/classes/TemplateTest.php +++ b/test/classes/TemplateTest.php @@ -30,6 +30,8 @@ class TemplateTest extends AbstractTestCase { global $cfg; + $this->loadContainerBuilder(); + $cfg['environment'] = 'production'; $twig = Template::getTwigEnvironment(null); $this->assertFalse($twig->isDebug()); From c0e85ed76c4fa24381c3bb88bfb38194d701c965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Sat, 20 Mar 2021 18:56:35 -0300 Subject: [PATCH 3/3] Add flash message support for db structure controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- .../Database/StructureController.php | 18 +++++----- libraries/services_controllers.php | 1 + templates/database/structure/empty_form.twig | 2 +- templates/database/structure/index.twig | 8 +++++ .../Database/StructureControllerTest.php | 35 +++++++++++++------ 5 files changed, 44 insertions(+), 20 deletions(-) diff --git a/libraries/classes/Controllers/Database/StructureController.php b/libraries/classes/Controllers/Database/StructureController.php index e8f3b58e55..16d8f22205 100644 --- a/libraries/classes/Controllers/Database/StructureController.php +++ b/libraries/classes/Controllers/Database/StructureController.php @@ -10,6 +10,7 @@ use PhpMyAdmin\Config\PageSettings; use PhpMyAdmin\Core; use PhpMyAdmin\Database\CentralColumns; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FlashMessages; use PhpMyAdmin\Html\Generator; use PhpMyAdmin\Message; use PhpMyAdmin\Operations; @@ -91,6 +92,9 @@ class StructureController extends AbstractController /** @var DatabaseInterface */ private $dbi; + /** @var FlashMessages */ + private $flash; + /** * @param Response $response * @param string $db Database name @@ -106,7 +110,8 @@ class StructureController extends AbstractController $replication, RelationCleanup $relationCleanup, Operations $operations, - $dbi + $dbi, + FlashMessages $flash ) { parent::__construct($response, $template, $db); $this->relation = $relation; @@ -114,6 +119,7 @@ class StructureController extends AbstractController $this->relationCleanup = $relationCleanup; $this->operations = $operations; $this->dbi = $dbi; + $this->flash = $flash; $this->replicationInfo = new ReplicationInfo($this->dbi); } @@ -1552,15 +1558,9 @@ class StructureController extends AbstractController $selected = $_POST['selected'] ?? []; if ($mult_btn !== __('Yes')) { - $message = Message::success(__('No change')); + $this->flash->addMessage('success', __('No change')); - if (empty($_POST['message'])) { - $_POST['message'] = Message::success(); - } - - unset($_POST['mult_btn']); - - $this->index(); + Core::sendHeaderLocation('./index.php?route=/database/structure' . Url::getCommonRaw(['db' => $db], '&')); return; } diff --git a/libraries/services_controllers.php b/libraries/services_controllers.php index 5c6f906bf7..411823b876 100644 --- a/libraries/services_controllers.php +++ b/libraries/services_controllers.php @@ -201,6 +201,7 @@ return [ '$relationCleanup' => '@relation_cleanup', '$operations' => '@operations', '$dbi' => '@dbi', + '$flash' => '@flash', ], ], PhpMyAdmin\Controllers\Database\TrackingController::class => [ diff --git a/templates/database/structure/empty_form.twig b/templates/database/structure/empty_form.twig index 582d07e6fa..0cf561c165 100644 --- a/templates/database/structure/empty_form.twig +++ b/templates/database/structure/empty_form.twig @@ -1,4 +1,4 @@ -
+ {{ get_hidden_inputs(url_params) }}
diff --git a/templates/database/structure/index.twig b/templates/database/structure/index.twig index 9cfe5e7d75..958b1e757d 100644 --- a/templates/database/structure/index.twig +++ b/templates/database/structure/index.twig @@ -1,3 +1,11 @@ +{% for flash_key, flash_messages in flash() %} + {% for flash_message in flash_messages %} + + {% endfor %} +{% endfor %} + {% if has_tables %}
{{ list_navigator_html|raw }} diff --git a/test/classes/Controllers/Database/StructureControllerTest.php b/test/classes/Controllers/Database/StructureControllerTest.php index 2b8238b4e8..6a62e1f095 100644 --- a/test/classes/Controllers/Database/StructureControllerTest.php +++ b/test/classes/Controllers/Database/StructureControllerTest.php @@ -11,6 +11,7 @@ namespace PhpMyAdmin\Tests\Controllers\Database; use PhpMyAdmin\Controllers\Database\StructureController; use PhpMyAdmin\DatabaseInterface; +use PhpMyAdmin\FlashMessages; use PhpMyAdmin\Operations; use PhpMyAdmin\RecentFavoriteTable; use PhpMyAdmin\Relation; @@ -53,6 +54,9 @@ class StructureControllerTest extends AbstractTestCase /** @var Operations */ private $operations; + /** @var FlashMessages */ + private $flash; + /** * Prepares environment for the test. */ @@ -95,6 +99,7 @@ class StructureControllerTest extends AbstractTestCase $this->replication = new Replication(); $this->relationCleanup = new RelationCleanup($dbi, $this->relation); $this->operations = new Operations($dbi, $this->relation); + $this->flash = new FlashMessages(); } /** @@ -113,7 +118,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); // Showing statistics $property = $class->getProperty('isShowStats'); @@ -173,7 +179,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); $currentTable['ENGINE'] = 'InnoDB'; @@ -216,7 +223,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); // Showing statistics $property = $class->getProperty('isShowStats'); @@ -280,7 +288,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); [$currentTable, , , , , , $sumSize] = $method->invokeArgs( $controller, @@ -304,7 +313,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); [$currentTable] = $method->invokeArgs( $controller, @@ -338,7 +348,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); // When parameter $db is empty @@ -376,7 +387,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); $_SESSION['tmpval']['favoriteTables'][$GLOBALS['server']] = [ @@ -414,7 +426,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); // The user hash for test @@ -474,7 +487,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); // Showing statistics $class = new ReflectionClass(StructureController::class); @@ -524,7 +538,8 @@ class StructureControllerTest extends AbstractTestCase $this->replication, $this->relationCleanup, $this->operations, - $GLOBALS['dbi'] + $GLOBALS['dbi'], + $this->flash ); // Showing statistics $class = new ReflectionClass(StructureController::class);