]> BookStack Code Mirror - hacks/blob - content/notify-tagged-page-updates/functions.php
Added license and hacks from gists
[hacks] / content / notify-tagged-page-updates / functions.php
1 <?php
2
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;
12
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.
20
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 {
24
25     protected Page $page;
26     protected User $updater;
27
28     public function __construct(Page $page, User $updater)
29     {
30         $this->page = $page;
31         $this->updater = $updater;
32     }
33
34     public function toMail($notifiable)
35     {
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());
40     }
41 }
42
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) {
46
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')
53         ->first();
54     if (!$notifyTag) {
55         return;
56     }
57
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()) {
62         return;
63     }
64
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')
69                  ->from('role_user')
70                  ->whereColumn('users.id', '=', 'role_user.user_id')
71                  ->whereIn('role_id', $roleIds);
72         })
73         ->where('id', '!=', $page->updated_by)
74         ->get();
75     $updater = User::query()->findOrFail($page->updated_by);
76
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
81     }
82 }
83
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);
88     }
89 });