diff --git a/app/Console/Commands/UpdateUrl.php b/app/Console/Commands/UpdateUrl.php index 0c27f819c..2a1688468 100644 --- a/app/Console/Commands/UpdateUrl.php +++ b/app/Console/Commands/UpdateUrl.php @@ -4,6 +4,7 @@ namespace BookStack\Console\Commands; use Illuminate\Console\Command; use Illuminate\Database\Connection; +use Illuminate\Support\Facades\DB; class UpdateUrl extends Command { @@ -66,17 +67,44 @@ class UpdateUrl extends Command foreach ($columnsToUpdateByTable as $table => $columns) { foreach ($columns as $column) { - $changeCount = $this->db->table($table)->update([ - $column => $this->db->raw("REPLACE({$column}, '{$oldUrl}', '{$newUrl}')") - ]); + $changeCount = $this->replaceValueInTable($table, $column, $oldUrl, $newUrl); $this->info("Updated {$changeCount} rows in {$table}->{$column}"); } } + $jsonColumnsToUpdateByTable = [ + "settings" => ["value"], + ]; + + foreach ($jsonColumnsToUpdateByTable as $table => $columns) { + foreach ($columns as $column) { + $oldJson = trim(json_encode($oldUrl), '"'); + $newJson = trim(json_encode($newUrl), '"'); + $changeCount = $this->replaceValueInTable($table, $column, $oldJson, $newJson); + $this->info("Updated {$changeCount} JSON encoded rows in {$table}->{$column}"); + } + } + $this->info("URL update procedure complete."); + $this->info('============================================================================'); + $this->info('Be sure to run "php artisan cache:clear" to clear any old URLs in the cache.'); + $this->info('============================================================================'); return 0; } + /** + * Perform a find+replace operations in the provided table and column. + * Returns the count of rows changed. + */ + protected function replaceValueInTable(string $table, string $column, string $oldUrl, string $newUrl): int + { + $oldQuoted = $this->db->getPdo()->quote($oldUrl); + $newQuoted = $this->db->getPdo()->quote($newUrl); + return $this->db->table($table)->update([ + $column => $this->db->raw("REPLACE({$column}, {$oldQuoted}, {$newQuoted})") + ]); + } + /** * Warn the user of the dangers of this operation. * Returns a boolean indicating if they've accepted the warnings. diff --git a/tests/Commands/UpdateUrlCommandTest.php b/tests/Commands/UpdateUrlCommandTest.php index 741040f1f..7043ce047 100644 --- a/tests/Commands/UpdateUrlCommandTest.php +++ b/tests/Commands/UpdateUrlCommandTest.php @@ -6,7 +6,7 @@ use Tests\TestCase; class UpdateUrlCommandTest extends TestCase { - public function test_update_url_command_updates_page_content() + public function test_command_updates_page_content() { $page = Page::query()->first(); $page->html = ''; @@ -22,7 +22,7 @@ class UpdateUrlCommandTest extends TestCase ]); } - public function test_update_url_command_requires_valid_url() + public function test_command_requires_valid_url() { $badUrlMessage = "The given urls are expected to be full urls starting with http:// or https://"; $this->artisan('bookstack:update-url //example.com https://cats.example.com')->expectsOutput($badUrlMessage); @@ -33,14 +33,27 @@ class UpdateUrlCommandTest extends TestCase $this->artisan('bookstack:update-url https://cats.example.com'); } - public function test_update_url_command_updates_settings() + public function test_command_updates_settings() { setting()->put('my-custom-item', 'https://example.com/donkey/cat'); - $this->artisan('bookstack:update-url https://example.com https://cats.example.com') - ->expectsQuestion("This will search for \"https://example.com\" in your database and replace it with \"https://cats.example.com\".\nAre you sure you want to proceed?", 'y') - ->expectsQuestion("This operation could cause issues if used incorrectly. Have you made a backup of your existing database?", 'y'); + $this->runUpdate('https://example.com', 'https://cats.example.com'); $settingVal = setting('my-custom-item'); $this->assertEquals('https://cats.example.com/donkey/cat', $settingVal); } + + public function test_command_updates_array_settings() + { + setting()->put('my-custom-array-item', [['name' => 'a https://example.com/donkey/cat url']]); + $this->runUpdate('https://example.com', 'https://cats.example.com'); + $settingVal = setting('my-custom-array-item'); + $this->assertEquals('a https://cats.example.com/donkey/cat url', $settingVal[0]['name']); + } + + protected function runUpdate(string $oldUrl, string $newUrl) + { + $this->artisan("bookstack:update-url {$oldUrl} {$newUrl}") + ->expectsQuestion("This will search for \"{$oldUrl}\" in your database and replace it with \"{$newUrl}\".\nAre you sure you want to proceed?", 'y') + ->expectsQuestion("This operation could cause issues if used incorrectly. Have you made a backup of your existing database?", 'y'); + } } \ No newline at end of file