3 use BookStack\Actions\ActivityType;
4 use BookStack\Actions\Tag;
5 use BookStack\Auth\Role;
6 use BookStack\Auth\User;
7 use BookStack\Entities\Models\Page;
8 use BookStack\Facades\Theme;
9 use BookStack\Notifications\MailNotification;
10 use BookStack\Theming\ThemeEvents;
11 use Illuminate\Notifications\Messages\MailMessage;
13 // This customization notifies page updates to users within roles named via a tag on a parent book.
14 // For example, If a tag, with name `Notify` and value `Admins, Viewers` is applied to a book, updates to pages within
15 // will be notified via email to all users within the "Admins" and "Viewers" roles.
16 // Note: This is not officially supported, may break upon update, and the email sending may slow down operations.
17 // Also, users could be spammed with emails on repeated updates.
18 // Also, might hit email system rate-limits.
19 // Also, this relies on role names being stable.
21 // This notification class represents the notification that'll be sent to users.
22 // The text of the notification can be customized within the 'toMail' function.
23 class PageUpdatedNotification extends MailNotification {
26 protected User $updater;
28 public function __construct(Page $page, User $updater)
31 $this->updater = $updater;
34 public function toMail($notifiable)
36 return (new MailMessage())
37 ->subject('BookStack page update notification')
38 ->line("The page \"{$this->page->name}\" has been updated by \"{$this->updater->name}\"")
39 ->action('View Page', $this->page->getUrl());
43 // This function does the work of sending notifications to the
44 // relevant users that are in roles denoted by a tag on the parent book.
45 function notifyRequiredUsers(Page $page) {
47 // Get our relevant tag
48 /** @var ?Tag $notifyTag */
49 $notifyTag = Tag::query()
50 ->where('entity_type', '=', 'book')
51 ->where('entity_id', '=', $page->book_id)
52 ->where('name', '=', 'notify')
58 // Get the roles named via the tag
59 $roleNames = array_filter(array_map(fn($str) => trim($str) , explode(',', $notifyTag->value)));
60 $roleIds = Role::query()->whereIn('display_name', $roleNames)->pluck('id');
61 if (count($roleNames) === 0 || $roleIds->isEmpty()) {
65 // Get the users we want to notify, and the user that updated the page
66 $usersToNotify = User::query()
67 ->whereExists(function($query) use ($roleIds) {
68 $query->select('user_id')
70 ->whereColumn('users.id', '=', 'role_user.user_id')
71 ->whereIn('role_id', $roleIds);
73 ->where('id', '!=', $page->updated_by)
75 $updater = User::query()->findOrFail($page->updated_by);
77 // Send a notification to each of the users we want to notify
78 foreach ($usersToNotify as $user) {
79 $user->notify(new PageUpdatedNotification($page, $updater));
80 usleep(100000); // Slight 0.1s delay to help rate limit abuse
84 // Listen to page update events and kick-start our notification logic
85 Theme::listen(ThemeEvents::ACTIVITY_LOGGED, function(string $type, $detail) {
86 if ($type === ActivityType::PAGE_UPDATE && $detail instanceof Page) {
87 notifyRequiredUsers($detail);