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