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!

Notify Page Updates for Tagged Books

  • Author: @ssddanbrown
  • Created: 1st Dec 2022
  • Updated: 20th Nov 2023
  • Last Tested On: v23.10.4

This allows you to configure notifications to be sent to users within roles defined via tags applied to parent books. For example, if a tag with name Notify and value Admins, Viewers is applied to a book, updates to pages within will be notified via email to all users within the “Admins” and “Viewers” roles.

Considerations

  • The sending of emails may slow down page update actions, and these could be noisy if a user edits a page many times quickly.
  • You may run into email system rate-limits with the amount of emails being sent.
  • By default, languages/translations are not handled in this example.
  • This could be abused to send a mass of emails to user groups.
  • You may prefer to use the in-platform notification system which was added in BookStack v23.08.

Options

  • You can customize the email message, if desired, by editing the lines of text within the toMail part at around lines 31-34 of the functions.php code.

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<?php

use BookStack\Activity\ActivityType;
use BookStack\Activity\Models\Tag;
use BookStack\Activity\Notifications\Messages\BaseActivityNotification;
use BookStack\Entities\Models\Page;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\Role;
use BookStack\Users\Models\User;
use Illuminate\Notifications\Messages\MailMessage;

// This customization notifies page updates to users within roles named via a tag  on a parent book.
// For example, If a tag, with name `Notify` and value `Admins, Viewers` is applied to a book, updates to pages within
// will be notified via email to all users within the "Admins" and "Viewers" roles.
// Note: This is not officially supported, may break upon update, and the email sending may slow down operations.
//       Also, users could be spammed with emails on repeated updates.
//       Also, might hit email system rate-limits.
//       Also, this relies on role names being stable.

// This notification class represents the notification that'll be sent to users.
// The text of the notification can be customized within the 'toMail' function.
class PageUpdateNotification extends BaseActivityNotification {
    public function toMail(User $notifiable): MailMessage
    {
        /** @var Page $page */
        $page = $this->detail;
        $updater = $this->user;

        return (new MailMessage())
            ->subject('BookStack page update notification')
            ->line("The page \"{$page->name}\" has been updated by \"{$updater->name}\"")
            ->action('View Page', $page->getUrl());
    }
}

// This function does the work of sending notifications to the
// relevant users that are in roles denoted by a tag on the parent book.
function notifyRequiredUsers(Page $page) {

    // Get our relevant tag
    /** @var ?Tag $notifyTag */
    $notifyTag = Tag::query()
        ->where('entity_type', '=', 'book')
        ->where('entity_id', '=', $page->book_id)
        ->where('name', '=', 'notify')
        ->first();
    if (!$notifyTag) {
        return;
    }

    // Get the roles named via the tag
    $roleNames = array_filter(array_map(fn ($str) => trim($str), explode(',', $notifyTag->value)));
    $roleIds = Role::query()->whereIn('display_name', $roleNames)->pluck('id');
    if (count($roleNames) === 0 || $roleIds->isEmpty()) {
        return;
    }

    // Get the users we want to notify, and the user that updated the page
    $usersToNotify = User::query()
        ->whereExists(function ($query) use ($roleIds) {
            $query->select('user_id')
                ->from('role_user')
                ->whereColumn('users.id', '=', 'role_user.user_id')
                ->whereIn('role_id', $roleIds);
        })
        ->where('id', '!=', $page->updated_by)
        ->get();
    $updater = User::query()->findOrFail($page->updated_by);

    // Send a notification to each of the users we want to notify
    foreach ($usersToNotify as $user) {
        $user->notify(new PageUpdateNotification($page, $updater));
        usleep(100000); // Slight 0.1s delay to help rate limit abuse
    }
}

// Listen to page update events and kick-start our notification logic
Theme::listen(ThemeEvents::ACTIVITY_LOGGED, function (string $type, $detail) {
    if ($type === ActivityType::PAGE_UPDATE && $detail instanceof Page) {
        notifyRequiredUsers($detail);
    }
});

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