]> BookStack Code Mirror - bookstack/blob - app/Activity/Tools/ActivityLogger.php
adda36c1b813a3f2728ae4c3eec64416fd1e8b64
[bookstack] / app / Activity / Tools / ActivityLogger.php
1 <?php
2
3 namespace BookStack\Activity\Tools;
4
5 use BookStack\Activity\DispatchWebhookJob;
6 use BookStack\Activity\Models\Activity;
7 use BookStack\Activity\Models\Loggable;
8 use BookStack\Activity\Models\Webhook;
9 use BookStack\Activity\Notifications\NotificationManager;
10 use BookStack\Entities\Models\Entity;
11 use BookStack\Facades\Theme;
12 use BookStack\Theming\ThemeEvents;
13 use Illuminate\Database\Eloquent\Builder;
14 use Illuminate\Support\Facades\Log;
15
16 class ActivityLogger
17 {
18     public function __construct(
19         protected NotificationManager $notifications
20     ) {
21         $this->notifications->loadDefaultHandlers();
22     }
23
24     /**
25      * Add a generic activity event to the database.
26      */
27     public function add(string $type, string|Loggable $detail = ''): void
28     {
29         $detailToStore = ($detail instanceof Loggable) ? $detail->logDescriptor() : $detail;
30
31         $activity = $this->newActivityForUser($type);
32         $activity->detail = $detailToStore;
33
34         if ($detail instanceof Entity) {
35             $activity->entity_id = $detail->id;
36             $activity->entity_type = $detail->getMorphClass();
37         }
38
39         $activity->save();
40
41         $this->setNotification($type);
42         $this->dispatchWebhooks($type, $detail);
43         $this->notifications->handle($activity, $detail, user());
44         Theme::dispatch(ThemeEvents::ACTIVITY_LOGGED, $type, $detail);
45     }
46
47     /**
48      * Get a new activity instance for the current user.
49      */
50     protected function newActivityForUser(string $type): Activity
51     {
52         return (new Activity())->forceFill([
53             'type'     => strtolower($type),
54             'user_id'  => user()->id,
55             'ip'       => IpFormatter::fromCurrentRequest()->format(),
56         ]);
57     }
58
59     /**
60      * Removes the entity attachment from each of its activities
61      * and instead uses the 'extra' field with the entities name.
62      * Used when an entity is deleted.
63      */
64     public function removeEntity(Entity $entity): void
65     {
66         $entity->activity()->update([
67             'detail'       => $entity->name,
68             'entity_id'    => null,
69             'entity_type'  => null,
70         ]);
71     }
72
73     /**
74      * Flashes a notification message to the session if an appropriate message is available.
75      */
76     protected function setNotification(string $type): void
77     {
78         $notificationTextKey = 'activities.' . $type . '_notification';
79         if (trans()->has($notificationTextKey)) {
80             $message = trans($notificationTextKey);
81             session()->flash('success', $message);
82         }
83     }
84
85     protected function dispatchWebhooks(string $type, string|Loggable $detail): void
86     {
87         $webhooks = Webhook::query()
88             ->whereHas('trackedEvents', function (Builder $query) use ($type) {
89                 $query->where('event', '=', $type)
90                     ->orWhere('event', '=', 'all');
91             })
92             ->where('active', '=', true)
93             ->get();
94
95         foreach ($webhooks as $webhook) {
96             dispatch(new DispatchWebhookJob($webhook, $type, $detail));
97         }
98     }
99
100     /**
101      * Log out a failed login attempt, Providing the given username
102      * as part of the message if the '%u' string is used.
103      */
104     public function logFailedLogin(string $username): void
105     {
106         $message = config('logging.failed_login.message');
107         if (!$message) {
108             return;
109         }
110
111         $message = str_replace('%u', $username, $message);
112         $channel = config('logging.failed_login.channel');
113         Log::channel($channel)->warning($message);
114     }
115 }