]> BookStack Code Mirror - hacks/blob - content/notify-tagged-page-updates/functions.php
Updated "notify-tagged-page-updates" hack for v23.10.4
[hacks] / content / notify-tagged-page-updates / functions.php
1 <?php
2
3 use BookStack\Activity\ActivityType;
4 use BookStack\Activity\Models\Tag;
5 use BookStack\Activity\Notifications\Messages\BaseActivityNotification;
6 use BookStack\Entities\Models\Page;
7 use BookStack\Facades\Theme;
8 use BookStack\Theming\ThemeEvents;
9 use BookStack\Users\Models\Role;
10 use BookStack\Users\Models\User;
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 PageUpdateNotification extends BaseActivityNotification {
24     public function toMail(User $notifiable): MailMessage
25     {
26         /** @var Page $page */
27         $page = $this->detail;
28         $updater = $this->user;
29
30         return (new MailMessage())
31             ->subject('BookStack page update notification')
32             ->line("The page \"{$page->name}\" has been updated by \"{$updater->name}\"")
33             ->action('View Page', $page->getUrl());
34     }
35 }
36
37 // This function does the work of sending notifications to the
38 // relevant users that are in roles denoted by a tag on the parent book.
39 function notifyRequiredUsers(Page $page) {
40
41     // Get our relevant tag
42     /** @var ?Tag $notifyTag */
43     $notifyTag = Tag::query()
44         ->where('entity_type', '=', 'book')
45         ->where('entity_id', '=', $page->book_id)
46         ->where('name', '=', 'notify')
47         ->first();
48     if (!$notifyTag) {
49         return;
50     }
51
52     // Get the roles named via the tag
53     $roleNames = array_filter(array_map(fn ($str) => trim($str), explode(',', $notifyTag->value)));
54     $roleIds = Role::query()->whereIn('display_name', $roleNames)->pluck('id');
55     if (count($roleNames) === 0 || $roleIds->isEmpty()) {
56         return;
57     }
58
59     // Get the users we want to notify, and the user that updated the page
60     $usersToNotify = User::query()
61         ->whereExists(function ($query) use ($roleIds) {
62             $query->select('user_id')
63                 ->from('role_user')
64                 ->whereColumn('users.id', '=', 'role_user.user_id')
65                 ->whereIn('role_id', $roleIds);
66         })
67         ->where('id', '!=', $page->updated_by)
68         ->get();
69     $updater = User::query()->findOrFail($page->updated_by);
70
71     // Send a notification to each of the users we want to notify
72     foreach ($usersToNotify as $user) {
73         $user->notify(new PageUpdateNotification($page, $updater));
74         usleep(100000); // Slight 0.1s delay to help rate limit abuse
75     }
76 }
77
78 // Listen to page update events and kick-start our notification logic
79 Theme::listen(ThemeEvents::ACTIVITY_LOGGED, function (string $type, $detail) {
80     if ($type === ActivityType::PAGE_UPDATE && $detail instanceof Page) {
81         notifyRequiredUsers($detail);
82     }
83 });