3 namespace BookStack\Activity\Tools;
5 use BookStack\Activity\ActivityType;
6 use BookStack\Activity\Models\Loggable;
7 use BookStack\Activity\Models\Webhook;
8 use BookStack\App\Model;
9 use BookStack\Entities\Models\Entity;
10 use BookStack\Entities\Models\Page;
11 use BookStack\Users\Models\User;
12 use Illuminate\Support\Carbon;
14 class WebhookFormatter
16 protected Webhook $webhook;
17 protected string $event;
18 protected User $initiator;
19 protected int $initiatedTime;
20 protected string|Loggable $detail;
23 * @var array{condition: callable(string, Model):bool, format: callable(Model):void}[]
25 protected $modelFormatters = [];
27 public function __construct(string $event, Webhook $webhook, string|Loggable $detail, User $initiator, int $initiatedTime)
29 $this->webhook = $webhook;
30 $this->event = $event;
31 $this->initiator = $initiator;
32 $this->initiatedTime = $initiatedTime;
33 $this->detail = is_object($detail) ? clone $detail : $detail;
36 public function format(): array
39 'event' => $this->event,
40 'text' => $this->formatText(),
41 'triggered_at' => Carbon::createFromTimestampUTC($this->initiatedTime)->toISOString(),
42 'triggered_by' => $this->initiator->attributesToArray(),
43 'triggered_by_profile_url' => $this->initiator->getProfileUrl(),
44 'webhook_id' => $this->webhook->id,
45 'webhook_name' => $this->webhook->name,
48 if (method_exists($this->detail, 'getUrl')) {
49 $data['url'] = $this->detail->getUrl();
52 if ($this->detail instanceof Model) {
53 $data['related_item'] = $this->formatModel();
60 * @param callable(string, Model):bool $condition
61 * @param callable(Model):void $format
63 public function addModelFormatter(callable $condition, callable $format): void
65 $this->modelFormatters[] = [
66 'condition' => $condition,
71 public function addDefaultModelFormatters(): void
73 // Load entity owner, creator, updater details
74 $this->addModelFormatter(
75 fn ($event, $model) => ($model instanceof Entity),
76 fn ($model) => $model->load(['ownedBy', 'createdBy', 'updatedBy'])
79 // Load revision detail for page update and create events
80 $this->addModelFormatter(
81 fn ($event, $model) => ($model instanceof Page && ($event === ActivityType::PAGE_CREATE || $event === ActivityType::PAGE_UPDATE)),
82 fn ($model) => $model->load('currentRevision')
86 protected function formatModel(): array
88 /** @var Model $model */
89 $model = $this->detail;
90 $model->unsetRelations();
92 foreach ($this->modelFormatters as $formatter) {
93 if ($formatter['condition']($this->event, $model)) {
94 $formatter['format']($model);
98 return $model->toArray();
101 protected function formatText(): string
104 $this->initiator->name,
105 trans('activities.' . $this->event),
108 if ($this->detail instanceof Entity) {
109 $textParts[] = '"' . $this->detail->name . '"';
112 return implode(' ', $textParts);
115 public static function getDefault(string $event, Webhook $webhook, $detail, User $initiator, int $initiatedTime): self
117 $instance = new self($event, $webhook, $detail, $initiator, $initiatedTime);
118 $instance->addDefaultModelFormatters();