Hacks & Customizations
These hacks are unsupported customizations meant as unofficial workarounds.
They can cause instability, introduce issues and may conflict with future updates. Apply at your own risk!

Prune Revisions Command

  • Author: @ssddanbrown
  • Created: 23rd May 2024
  • Updated: 23rd May 2024
  • Last Tested On: v24.05.1

This hack registers a custom command using the logical theme system, which will prune the revisions of a specific page to just those with a changelog provided (in addition to the current revision), before resetting the revision numbers across the remaining versions to be sequential without gaps. This will also reset the overall revision count on the page.

Once added, the command can be called like so from your BookStack install directory:

1
2
3
4
5
# Format
php artisan bookstack:prune-revisions {book-slug} {page-slug}

# Example
php artisan bookstack:prune-revisions team-notes code-snippets

Considerations

  • The changes here will not be reflected in activity/audit logs, and existing related activity/audit logs will not be modified.
  • This can delete data with no option to restore without resorting to backups.
  • This could affect other features or processes that may potentially reference specific page revisions or numbers.

Code

functions.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php

use BookStack\Entities\Models\Page;
use BookStack\Facades\Theme;
use Illuminate\Console\Command;
use Illuminate\Database\Eloquent\Builder;

// Define the command we want to add to BookStack
class PruneRevisionsCommand extends Command
{
    // Specify how the command should be called, and the description that'll show for it
    protected $signature = 'bookstack:prune-revisions {bookslug} {pageslug}';
    protected $description = 'Remove revisions without changelog and reset numbers for a specific page';

    // Define the function that'll be called when the command is performed
    public function handle(): int
    {
        $pageSlug = $this->argument('pageslug');
        $bookSlug = $this->argument('bookslug');

        // Find the provided page using slugs
        $page = Page::query()->whereHas('book', function (Builder $query) use ($bookSlug) {
            $query->where('slug', '=', $bookSlug);
        })->where('slug', '=', $pageSlug)->first();

        // Return as an error if the page is not found
        if (!$page) {
            $this->error("Page of slugs {$bookSlug}:{$pageSlug} not found");
            return 1;
        }

        // Delete revisions without changelog, except if it's the current revision
        $currentRevision = $page->currentRevision()->first();
        $deleteCount = $page->allRevisions()
            ->where('type', '=', 'version')
            ->where('summary', '=', '')
            ->where('id', '!=', $currentRevision->id)
            ->delete();

        // Reset numbers on the revisions
        $revisions = $page->allRevisions()->where('type', '=', 'version')
            ->orderBy('created_at', 'asc')
            ->get()
            ->all();
        foreach ($revisions as $index => $revision) {
            $revision->revision_number = $index + 1;
            $revision->save();
        }

        // Update page revision count
        $page->revision_count = count($revisions);
        $page->save();

        $this->line("Revisions pruned and re-numbered for page [{$page->name}] with {$deleteCount} revisions deleted");

        return 0;
    }
}

// Register our command via the theme system
Theme::registerCommand(new PruneRevisionsCommand());

Request an Update

Hack not working on the latest version of BookStack?
You can request this hack to be updated & tested for a small one-time fee.
This helps keeps these hacks updated & maintained in a sustainable manner.


Latest Hacks