3 namespace BookStack\Activity;
5 use BookStack\Activity\Models\Comment;
6 use BookStack\Entities\Models\Entity;
7 use BookStack\Entities\Models\Page;
8 use BookStack\Exceptions\NotifyException;
9 use BookStack\Facades\Activity as ActivityService;
10 use BookStack\Util\HtmlDescriptionFilter;
11 use Illuminate\Database\Eloquent\Builder;
16 * Get a comment by ID.
18 public function getById(int $id): Comment
20 return Comment::query()->findOrFail($id);
24 * Get a comment by ID, ensuring it is visible to the user based upon access to the page
25 * which the comment is attached to.
27 public function getVisibleById(int $id): Comment
29 return $this->getQueryForVisible()->findOrFail($id);
33 * Start a query for comments visible to the user.
35 public function getQueryForVisible(): Builder
37 return Comment::query()->scopes('visible');
41 * Create a new comment on an entity.
43 public function create(Entity $entity, string $html, ?int $parentId, string $contentRef): Comment
45 // Prevent comments being added to draft pages
46 if ($entity instanceof Page && $entity->draft) {
47 throw new \Exception(trans('errors.cannot_add_comment_to_draft'));
51 if ($parentId !== null) {
52 $parentCommentExists = Comment::query()
53 ->where('entity_id', '=', $entity->id)
54 ->where('entity_type', '=', $entity->getMorphClass())
55 ->where('local_id', '=', $parentId)
57 if (!$parentCommentExists) {
63 $comment = new Comment();
65 $comment->html = HtmlDescriptionFilter::filterFromString($html);
66 $comment->created_by = $userId;
67 $comment->updated_by = $userId;
68 $comment->local_id = $this->getNextLocalId($entity);
69 $comment->parent_id = $parentId;
70 $comment->content_ref = preg_match('/^bkmrk-(.*?):\d+:(\d*-\d*)?$/', $contentRef) === 1 ? $contentRef : '';
72 $entity->comments()->save($comment);
73 ActivityService::add(ActivityType::COMMENT_CREATE, $comment);
74 ActivityService::add(ActivityType::COMMENTED_ON, $entity);
80 * Update an existing comment.
82 public function update(Comment $comment, string $html): Comment
84 $comment->updated_by = user()->id;
85 $comment->html = HtmlDescriptionFilter::filterFromString($html);
88 ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
95 * Archive an existing comment.
97 public function archive(Comment $comment, bool $log = true): Comment
99 if ($comment->parent_id) {
100 throw new NotifyException('Only top-level comments can be archived.', '/', 400);
103 $comment->archived = true;
107 ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
114 * Un-archive an existing comment.
116 public function unarchive(Comment $comment, bool $log = true): Comment
118 if ($comment->parent_id) {
119 throw new NotifyException('Only top-level comments can be un-archived.', '/', 400);
122 $comment->archived = false;
126 ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
133 * Delete a comment from the system.
135 public function delete(Comment $comment): void
139 ActivityService::add(ActivityType::COMMENT_DELETE, $comment);
143 * Get the next local ID relative to the linked entity.
145 protected function getNextLocalId(Entity $entity): int
147 $currentMaxId = $entity->comments()->max('local_id');
149 return $currentMaxId + 1;