]> BookStack Code Mirror - bookstack/commitdiff
Added "update-url" command to find/replace url in the database
authorDan Brown <redacted>
Thu, 9 Apr 2020 15:58:40 +0000 (16:58 +0100)
committerDan Brown <redacted>
Thu, 9 Apr 2020 15:59:26 +0000 (16:59 +0100)
- Also aligned format of command descriptions.

Targeted most common columns.
Have not done revisions for the sake of keeping that
content true to how it was originally stored but could
cause unexpected behaviour.

For #1225

app/Console/Commands/ClearViews.php
app/Console/Commands/CopyShelfPermissions.php
app/Console/Commands/DeleteUsers.php
app/Console/Commands/RegenerateSearch.php
app/Console/Commands/UpdateUrl.php [new file with mode: 0644]
tests/CommandsTest.php

index 678c64d330179893880f707db7cd0f8a705bf297..35356210b66809c62687e8fbb1eaa437bc447106 100644 (file)
@@ -18,7 +18,7 @@ class ClearViews extends Command
      *
      * @var string
      */
-    protected $description = 'Clear all view-counts for all entities.';
+    protected $description = 'Clear all view-counts for all entities';
 
     /**
      * Create a new command instance.
index d9a1c1d7282ed051b44f5f77d2c16525416d356e..6b5d35a476798a67d2bf16a42d2225b67363d632 100644 (file)
@@ -23,7 +23,7 @@ class CopyShelfPermissions extends Command
      *
      * @var string
      */
-    protected $description = 'Copy shelf permissions to all child books.';
+    protected $description = 'Copy shelf permissions to all child books';
 
     /**
      * @var BookshelfRepo
index 68c5bb738178f6b6fdfdbfeb0dec2d64dc5371f7..c73c883de2d2bd75535f817e5c10e03eff3e9ff4 100644 (file)
@@ -25,7 +25,7 @@ class DeleteUsers extends Command
      *
      * @var string
      */
-    protected $description = 'Delete users that are not "admin" or system users.';
+    protected $description = 'Delete users that are not "admin" or system users';
 
     public function __construct(User $user, UserRepo $userRepo)
     {
index d27d73edc01dcbf55443f9bbf8f916539d0c5295..dc57f2cea764b3a8517de9453ae4ee05fd86d9a6 100644 (file)
@@ -3,6 +3,7 @@
 namespace BookStack\Console\Commands;
 
 use BookStack\Entities\SearchService;
+use DB;
 use Illuminate\Console\Command;
 
 class RegenerateSearch extends Command
@@ -26,7 +27,7 @@ class RegenerateSearch extends Command
     /**
      * Create a new command instance.
      *
-     * @param \BookStack\Entities\SearchService $searchService
+     * @param SearchService $searchService
      */
     public function __construct(SearchService $searchService)
     {
@@ -41,14 +42,14 @@ class RegenerateSearch extends Command
      */
     public function handle()
     {
-        $connection = \DB::getDefaultConnection();
+        $connection = DB::getDefaultConnection();
         if ($this->option('database') !== null) {
-            \DB::setDefaultConnection($this->option('database'));
-            $this->searchService->setConnection(\DB::connection($this->option('database')));
+            DB::setDefaultConnection($this->option('database'));
+            $this->searchService->setConnection(DB::connection($this->option('database')));
         }
 
         $this->searchService->indexAllEntities();
-        \DB::setDefaultConnection($connection);
+        DB::setDefaultConnection($connection);
         $this->comment('Search index regenerated');
     }
 }
diff --git a/app/Console/Commands/UpdateUrl.php b/app/Console/Commands/UpdateUrl.php
new file mode 100644 (file)
index 0000000..b95e277
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+
+namespace BookStack\Console\Commands;
+
+use Illuminate\Console\Command;
+use Illuminate\Database\Connection;
+
+class UpdateUrl extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'bookstack:update-url
+                            {oldUrl : URL to replace}
+                            {newUrl : URL to use as the replacement}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Find and replace the given URLs in your BookStack database';
+
+    protected $db;
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct(Connection $db)
+    {
+        $this->db = $db;
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $oldUrl = str_replace("'", '', $this->argument('oldUrl'));
+        $newUrl = str_replace("'", '', $this->argument('newUrl'));
+
+        $urlPattern = '/https?:\/\/(.+)/';
+        if (!preg_match($urlPattern, $oldUrl) || !preg_match($urlPattern, $newUrl)) {
+            $this->error("The given urls are expected to be full urls starting with http:// or https://");
+            return 1;
+        }
+
+        if (!$this->checkUserOkayToProceed($oldUrl, $newUrl)) {
+            return 1;
+        }
+
+        $columnsToUpdateByTable = [
+            "attachments" => ["path"],
+            "pages" => ["html", "text", "markdown"],
+            "images" => ["url"],
+            "comments" => ["html", "text"],
+        ];
+
+        foreach ($columnsToUpdateByTable as $table => $columns) {
+            foreach ($columns as $column) {
+                $changeCount = $this->db->table($table)->update([
+                    $column => $this->db->raw("REPLACE({$column}, '{$oldUrl}', '{$newUrl}')")
+                ]);
+                $this->info("Updated {$changeCount} rows in {$table}->{$column}");
+            }
+        }
+
+        $this->info("URL update procedure complete.");
+        return 0;
+    }
+
+    /**
+     * Warn the user of the dangers of this operation.
+     * Returns a boolean indicating if they've accepted the warnings.
+     */
+    protected function checkUserOkayToProceed(string $oldUrl, string $newUrl): bool
+    {
+        $dangerWarning = "This will search for \"{$oldUrl}\" in your database and replace it with  \"{$newUrl}\".\n";
+        $dangerWarning .= "Are you sure you want to proceed?";
+        $backupConfirmation = "This operation could cause issues if used incorrectly. Have you made a backup of your existing database?";
+
+        return $this->confirm($dangerWarning) && $this->confirm($backupConfirmation);
+    }
+}
index 099af2939ddfd932aa1b5b9abb73544d3ce9adda..e55b047d49dfd7542ce160e466540c66d73ecf22 100644 (file)
@@ -5,6 +5,7 @@ use BookStack\Entities\Bookshelf;
 use BookStack\Entities\Page;
 use BookStack\Auth\User;
 use BookStack\Entities\Repos\PageRepo;
+use Symfony\Component\Console\Exception\RuntimeException;
 
 class CommandsTest extends TestCase
 {
@@ -166,4 +167,31 @@ class CommandsTest extends TestCase
         $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'view', 'role_id' => $editorRole->id]);
         $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'update', 'role_id' => $editorRole->id]);
     }
+
+    public function test_update_url_command_updates_page_content()
+    {
+        $page = Page::query()->first();
+        $page->html = '<a href="https://example.com/donkeys"></a>';
+        $page->save();
+
+        $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->assertDatabaseHas('pages', [
+            'id' => $page->id,
+            'html' => '<a href="https://cats.example.com/donkeys"></a>'
+        ]);
+    }
+
+    public function test_update_url_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);
+        $this->artisan('bookstack:update-url https://example.com htts://cats.example.com')->expectsOutput($badUrlMessage);
+        $this->artisan('bookstack:update-url example.com https://cats.example.com')->expectsOutput($badUrlMessage);
+
+        $this->expectException(RuntimeException::class);
+        $this->artisan('bookstack:update-url https://cats.example.com');
+    }
 }
Morty Proxy This is a proxified and sanitized view of the page, visit original site.