2021-12-12 01:29:33 +08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace BookStack\Actions;
|
|
|
|
|
|
|
|
use BookStack\Auth\Permissions\PermissionService;
|
|
|
|
use BookStack\Entities\Models\Entity;
|
|
|
|
use BookStack\Interfaces\Loggable;
|
2021-12-12 06:29:33 +08:00
|
|
|
use Illuminate\Database\Eloquent\Builder;
|
2021-12-12 01:29:33 +08:00
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
|
|
|
|
class ActivityLogger
|
|
|
|
{
|
|
|
|
protected $permissionService;
|
|
|
|
|
|
|
|
public function __construct(PermissionService $permissionService)
|
|
|
|
{
|
|
|
|
$this->permissionService = $permissionService;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a generic activity event to the database.
|
|
|
|
*
|
|
|
|
* @param string|Loggable $detail
|
|
|
|
*/
|
|
|
|
public function add(string $type, $detail = '')
|
|
|
|
{
|
|
|
|
$detailToStore = ($detail instanceof Loggable) ? $detail->logDescriptor() : $detail;
|
|
|
|
|
|
|
|
$activity = $this->newActivityForUser($type);
|
|
|
|
$activity->detail = $detailToStore;
|
|
|
|
|
|
|
|
if ($detail instanceof Entity) {
|
|
|
|
$activity->entity_id = $detail->id;
|
|
|
|
$activity->entity_type = $detail->getMorphClass();
|
|
|
|
}
|
|
|
|
|
|
|
|
$activity->save();
|
|
|
|
$this->setNotification($type);
|
2021-12-12 06:29:33 +08:00
|
|
|
$this->dispatchWebhooks($type, $detail);
|
2021-12-12 01:29:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a new activity instance for the current user.
|
|
|
|
*/
|
|
|
|
protected function newActivityForUser(string $type): Activity
|
|
|
|
{
|
|
|
|
$ip = request()->ip() ?? '';
|
|
|
|
|
|
|
|
return (new Activity())->forceFill([
|
|
|
|
'type' => strtolower($type),
|
|
|
|
'user_id' => user()->id,
|
|
|
|
'ip' => config('app.env') === 'demo' ? '127.0.0.1' : $ip,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the entity attachment from each of its activities
|
|
|
|
* and instead uses the 'extra' field with the entities name.
|
|
|
|
* Used when an entity is deleted.
|
|
|
|
*/
|
|
|
|
public function removeEntity(Entity $entity)
|
|
|
|
{
|
|
|
|
$entity->activity()->update([
|
|
|
|
'detail' => $entity->name,
|
|
|
|
'entity_id' => null,
|
|
|
|
'entity_type' => null,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flashes a notification message to the session if an appropriate message is available.
|
|
|
|
*/
|
2021-12-12 06:29:33 +08:00
|
|
|
protected function setNotification(string $type): void
|
2021-12-12 01:29:33 +08:00
|
|
|
{
|
|
|
|
$notificationTextKey = 'activities.' . $type . '_notification';
|
|
|
|
if (trans()->has($notificationTextKey)) {
|
|
|
|
$message = trans($notificationTextKey);
|
|
|
|
session()->flash('success', $message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-12 06:29:33 +08:00
|
|
|
/**
|
|
|
|
* @param string|Loggable $detail
|
|
|
|
*/
|
|
|
|
protected function dispatchWebhooks(string $type, $detail): void
|
|
|
|
{
|
2021-12-13 01:39:06 +08:00
|
|
|
$webhooks = Webhook::query()
|
2021-12-18 19:43:05 +08:00
|
|
|
->whereHas('trackedEvents', function (Builder $query) use ($type) {
|
2021-12-13 01:39:06 +08:00
|
|
|
$query->where('event', '=', $type)
|
|
|
|
->orWhere('event', '=', 'all');
|
|
|
|
})
|
|
|
|
->where('active', '=', true)
|
|
|
|
->get();
|
2021-12-12 06:29:33 +08:00
|
|
|
|
|
|
|
foreach ($webhooks as $webhook) {
|
|
|
|
dispatch(new DispatchWebhookJob($webhook, $type, $detail));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-12 01:29:33 +08:00
|
|
|
/**
|
|
|
|
* Log out a failed login attempt, Providing the given username
|
|
|
|
* as part of the message if the '%u' string is used.
|
|
|
|
*/
|
|
|
|
public function logFailedLogin(string $username)
|
|
|
|
{
|
|
|
|
$message = config('logging.failed_login.message');
|
|
|
|
if (!$message) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$message = str_replace('%u', $username, $message);
|
|
|
|
$channel = config('logging.failed_login.channel');
|
|
|
|
Log::channel($channel)->warning($message);
|
|
|
|
}
|
|
|
|
}
|