diff --git a/tests/webfiori/framework/test/PageTest.php b/tests/webfiori/framework/test/PageTest.php index 161aaf89c..ea41ab69a 100644 --- a/tests/webfiori/framework/test/PageTest.php +++ b/tests/webfiori/framework/test/PageTest.php @@ -154,7 +154,6 @@ public function testBeforeRender00() { $c->assertTrue($p->getDocument()->hasChild('super-el')); },0, [$this]); $this->assertNotNull($c); - $this->assertEquals(2, $c->getID()); $this->assertEquals(0, $c->getPriority()); $c2 = $page->addBeforeRender(function(WebPage $p, TestCase $c) @@ -162,9 +161,93 @@ public function testBeforeRender00() { $ch = $p->insert('div'); $ch->setID('super-el'); }, 3, [$this]); - $this->assertEquals(3, $c2->getID()); $this->assertEquals(3, $c2->getPriority()); } + /** + * @test + */ + public function testBeforeRender01() { + $page = new WebPage(); + $c = $page->addBeforeRender(function (WebPage $p, TestCase $c) + { + $c->assertTrue($p->getDocument()->getChildByID('super-el') !== null); + },0, [$this]); + + $c2 = $page->addBeforeRender(function(WebPage $p, TestCase $c) + { + $ch = $p->insert('div'); + $ch->setID('super-el'); + }, 3, [$this]); + $this->assertNull($page->getDocument()->getChildByID('super-el')); + $page->beforeRender(); + $this->assertNotNull($page->getDocument()->getChildByID('super-el')); + } + /** + * @test + */ + public function testBeforeRender02() { + $page = new WebPage(); + $c = $page->addBeforeRender(function (WebPage $p, TestCase $c) + { + $c->assertTrue($p->getDocument()->getChildByID('super-el') === null); + },0, [$this]); + + $c2 = $page->addBeforeRender(function(WebPage $p, TestCase $c) + { + $ch = $p->insert('div'); + $ch->setID('super-el'); + }, 3, [$this]); + $page->removeBeforeRender($c2->getID()); + $page->beforeRender(); + $this->assertNull($page->getDocument()->getChildByID('super-el')); + } + /** + * @test + */ + public function testBeforeRender03() { + $page = new WebPage(); + $c = $page->addBeforeRender(function (WebPage $p, TestCase $c) + { + + $p->addBeforeRender(function (WebPage $p, TestCase $c) { + $c->assertTrue($p->getDocument()->getChildByID('super-el') === null); + }, 4, [$this]); + $p->addBeforeRender(function(WebPage $p, TestCase $c) + { + $ch = $p->insert('div'); + $ch->setID('super-el'); + }, 3, [$this]); + $p->addBeforeRender(function (WebPage $p, TestCase $c) { + $c->assertTrue($p->getDocument()->getChildByID('super-el') !== null); + }, 2, [$this]); + + },0, [$this]); + + $page->beforeRender(); + $this->assertNotNull($page->getDocument()->getChildByID('super-el')); + } + /** + * @test + */ + public function testBeforeRender04() { + $page = new WebPage(); + + $c2 = $page->addBeforeRender(function(WebPage $p, TestCase $c) + { + $ch = $p->insert('div'); + $ch->setID('super-el'); + }, 3, [$this]); + + $c2->setCallback(function(WebPage $p, TestCase $c) + { + $ch = $p->insert('div'); + $ch->setID('super-cool'); + }, [$this]); + + $page->beforeRender(); + $this->assertNull($page->getDocument()->getChildByID('super-el')); + $this->assertNotNull($page->getDocument()->getChildByID('super-cool')); + } /** * @test */ diff --git a/tests/webfiori/framework/test/cli/AddCommandTest.php b/tests/webfiori/framework/test/cli/AddCommandTest.php index 50bd9b198..7e1d780ba 100644 --- a/tests/webfiori/framework/test/cli/AddCommandTest.php +++ b/tests/webfiori/framework/test/cli/AddCommandTest.php @@ -1,7 +1,7 @@ executeSingleCommand(new AddCommand(), [], [ + '3' + ]); $runner = new Runner(); $runner->setInputs([ '3' ]); - $this->assertEquals(0, $runner->runCommand(new AddCommand())); $this->assertEquals([ "What would you like to add?\n", "0: New database connection.\n", "1: New SMTP connection.\n", "2: New website language.\n", "3: Quit. <--\n" - ], $runner->getOutput()); + ], $output); + $this->assertEquals(0, $this->getExitCode()); } /** * @test */ public function testAddDBConnection00() { - $runner = App::getRunner(); - $runner->setInputs([ + $output = $this->executeSingleCommand(new AddCommand(), [], [ '0', '0', '127.0.0.1', @@ -46,11 +48,8 @@ public function testAddDBConnection00() { 'testing_db', '' ]); - $runner->setArgsVector([ - 'webfiori', - 'add' - ]); - $this->assertEquals(0, $runner->start()); + + $connName = 'db-connection-'.(count(App::getConfig()->getDBConnections()) - 1); $this->assertEquals([ "What would you like to add?\n", @@ -70,7 +69,8 @@ public function testAddDBConnection00() { "Trying to connect to the database...\n", "Success: Connected. Adding the connection...\n", "Success: Connection information was stored in application configuration.\n" - ], $runner->getOutput()); + ], $output); + $this->assertEquals(0, $this->getExitCode()); } /** * @test @@ -111,8 +111,9 @@ public function testAddDBConnection01() { "Database name:\n", "Give your connection a friendly name: Enter = '$connName'\n", "Trying to connect to the database...\n", + "Trying with 'localhost'...\n", "Error: Unable to connect to the database.\n", - "Error: Unable to connect to database: 2002 - No such file or directory\n", + "Error: Unable to connect to database: 1045 - Access denied for user 'root'@'localhost' (using password: YES)\n", "Would you like to store connection information anyway?(y/N)\n", "Success: Connection information was stored in application configuration.\n" ], $runner->getOutput()); @@ -155,8 +156,9 @@ public function testAddDBConnection02() { "Database name:\n", "Give your connection a friendly name: Enter = '$connName'\n", "Trying to connect to the database...\n", + "Trying with 'localhost'...\n", "Error: Unable to connect to the database.\n", - "Error: Unable to connect to database: 2002 - No such file or directory\n", + "Error: Unable to connect to database: 1045 - Access denied for user 'root'@'localhost' (using password: YES)\n", "Would you like to store connection information anyway?(y/N)\n", ], $runner->getOutput()); } diff --git a/webfiori/framework/App.php b/webfiori/framework/App.php index fac0232ad..0d291b456 100644 --- a/webfiori/framework/App.php +++ b/webfiori/framework/App.php @@ -637,7 +637,7 @@ public static function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_VERSION', '3.0.0-Beta.26'); + define('WF_VERSION', '3.0.0-Beta.27'); /** * A constant that tells the type of framework version. * @@ -653,7 +653,7 @@ public static function initFrameworkVersionInfo() { * * @since 2.1 */ - define('WF_RELEASE_DATE', '2025-04-07'); + define('WF_RELEASE_DATE', '2025-05-26'); } /** diff --git a/webfiori/framework/cli/commands/AddCommand.php b/webfiori/framework/cli/commands/AddCommand.php index c6d1a8130..2d6f15cc0 100644 --- a/webfiori/framework/cli/commands/AddCommand.php +++ b/webfiori/framework/cli/commands/AddCommand.php @@ -75,12 +75,16 @@ private function addDbConnection(): int { $this->println('Trying to connect to the database...'); $addConnection = $this->tryConnect($connInfoObj); - + $orgHost = $connInfoObj->getHost(); + $orgErr = $addConnection !== true ? $addConnection->getMessage() : ''; + if ($addConnection !== true) { if ($connInfoObj->getHost() == '127.0.0.1') { + $this->println("Trying with 'localhost'..."); $connInfoObj->setHost('localhost'); $addConnection = $this->tryConnect($connInfoObj); } else if ($connInfoObj->getHost() == 'localhost') { + $this->println("Trying with '127.0.0.1'..."); $connInfoObj->setHost('127.0.0.1'); $addConnection = $this->tryConnect($connInfoObj); } @@ -92,8 +96,9 @@ private function addDbConnection(): int { App::getConfig()->addOrUpdateDBConnection($connInfoObj); $this->success('Connection information was stored in application configuration.'); } else { + $connInfoObj->setHost($orgHost); $this->error('Unable to connect to the database.'); - $this->error($addConnection->getMessage()); + $this->error($orgErr); $this->confirmAdd($connInfoObj); } diff --git a/webfiori/framework/cli/commands/RunMigrationsCommand.php b/webfiori/framework/cli/commands/RunMigrationsCommand.php index 88b7eea7e..e6f4839cc 100644 --- a/webfiori/framework/cli/commands/RunMigrationsCommand.php +++ b/webfiori/framework/cli/commands/RunMigrationsCommand.php @@ -232,7 +232,19 @@ private function getDBConnection(?MigrationsRunner $runner = null) { return CLIUtils::getConnectionName($this); } } + public function getNext(MigrationsRunner $runner) : ?AbstractMigration { + foreach ($runner->getMigrations() as $m) { + if ($runner->isApplied($m->getName())) { + continue; + } else { + return $m; + } + } + return null; + } private function applyNext(MigrationsRunner $runner, &$listOfApplied) : bool { + $toBeApplied = $this->getNext($runner); + try { //$this->println("Executing migration..."); $applied = $runner->applyOne(); @@ -248,6 +260,15 @@ private function applyNext(MigrationsRunner $runner, &$listOfApplied) : bool { $this->error('Failed to execute migration due to following:'); $this->println($ex->getMessage().' (Line '.$ex->getLine().')'); $this->warning('Execution stopped.'); + $this->warning('Rolling back changes...'); + + try { + $toBeApplied->down($runner); + } catch (Throwable $exc) { + $this->error('Failed to rollback due to following:'); + $this->println($ex->getMessage().' (Line '.$ex->getLine().')'); + } + return false; } } diff --git a/webfiori/framework/ui/BeforeRenderCallback.php b/webfiori/framework/ui/BeforeRenderCallback.php index 8955fe977..16bf77b63 100644 --- a/webfiori/framework/ui/BeforeRenderCallback.php +++ b/webfiori/framework/ui/BeforeRenderCallback.php @@ -42,10 +42,22 @@ class BeforeRenderCallback implements Comparable { * */ public function __construct(callable $func, int $priority = 0, array $params = []) { + $this->setCallback($func, $params); + $this->setPriority($priority >= 0 ? $priority : 0); + $this->isExecuted = false; + $this->setID(hash('sha256', microtime().''.$priority.random_bytes(20))); + } + /** + * Sets the callback. + * + * @param callable $func A function to be executed. + * + * @param array $params An optional array that can hold extra parameters to + * pass to the callback. + */ + public function setCallback(callable $func, array $params = []) { $this->callback = $func; $this->params = $params; - $this->priority = $priority >= 0 ? $priority : 0; - $this->isExecuted = false; } /** * Execute the callback. @@ -75,9 +87,9 @@ public function compare($other): int { /** * Returns the identifier of the callback. * - * @return int The identifier of the callback. + * @return string The identifier of the callback. */ - public function getID() : int { + public function getID() : string { return $this->id; } /** @@ -104,9 +116,9 @@ public function isExecuted() : bool { /** * Sets a unique identifier for the callback. * - * @param int $id A unique identifier for the callback. + * @param string $id A unique identifier for the callback. */ - public function setID(int $id) { + public function setID(string $id) { $this->id = $id; } /** diff --git a/webfiori/framework/ui/WebPage.php b/webfiori/framework/ui/WebPage.php index bebd05585..64822fd79 100644 --- a/webfiori/framework/ui/WebPage.php +++ b/webfiori/framework/ui/WebPage.php @@ -236,9 +236,8 @@ public function __construct() { * * @since 1.0 */ - public function addBeforeRender(callable $callable, $priority = 0, array $params = []) : BeforeRenderCallback { + public function &addBeforeRender(callable $callable, $priority = 0, array $params = []) : BeforeRenderCallback { $beforeRender = new BeforeRenderCallback($callable, $priority, $params); - $beforeRender->setID($this->beforeRenderCallbacks->size() + 1); $this->beforeRenderCallbacks->add($beforeRender); return $beforeRender; @@ -728,6 +727,29 @@ public function insert($node, string $parentNodeId = self::MAIN_ELEMENTS[2]) { public function isThemeLoaded() : bool { return $this->theme instanceof Theme; } + /** + * Removes a callback given its ID. + * + * @param string $id The unique identifier of the callback. + * + * @return BeforeRenderCallback|null If removed, an object will be returned + * that holds its information. Other than that, null is returned. + */ + public function removeBeforeRender(string $id) : ?BeforeRenderCallback { + $index = -1; + $tempIndex = 0; + + foreach ($this->beforeRenderCallbacks as $callback) { + + if ($callback->getID() == $id) { + $index = $tempIndex; + break; + } + $tempIndex++; + } + + return $this->beforeRenderCallbacks->remove($index); + } /** * Removes a child node from the document of the page. * @@ -762,7 +784,7 @@ public function removeChild($node) { * @since 1.0 */ public function render(bool $formatted = false, bool $returnResult = false) { - $this->invokeBeforeRender(); + $this->beforeRender(); if (!$returnResult) { $formatted = $formatted === true || (defined('WF_VERBOSE') && WF_VERBOSE); @@ -1227,6 +1249,10 @@ private function getHead() { return $headNode; } + public function beforeRender() { + $this->beforeRenderCallbacks->insertionSort(false); + $this->invokeBeforeRender(); + } private function invokeBeforeRender(int $current = 0) { $currentCount = count($this->beforeRenderCallbacks); @@ -1237,6 +1263,9 @@ private function invokeBeforeRender(int $current = 0) { $newCount = count($this->beforeRenderCallbacks); if ($newCount != $currentCount) { + //This part is used to handel callbacks + //which are added during the process of executing + //callbacks $this->beforeRenderCallbacks->insertionSort(false); $this->invokeBeforeRender(); } else {