]> BookStack Code Mirror - bookstack/blob - app/Activity/CommentRepo.php
Copying: Fixed issue with non-page links to page permalinks
[bookstack] / app / Activity / CommentRepo.php
1 <?php
2
3 namespace BookStack\Activity;
4
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;
12
13 class CommentRepo
14 {
15     /**
16      * Get a comment by ID.
17      */
18     public function getById(int $id): Comment
19     {
20         return Comment::query()->findOrFail($id);
21     }
22
23     /**
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.
26      */
27     public function getVisibleById(int $id): Comment
28     {
29         return $this->getQueryForVisible()->findOrFail($id);
30     }
31
32     /**
33      * Start a query for comments visible to the user.
34      * @return Builder<Comment>
35      */
36     public function getQueryForVisible(): Builder
37     {
38         return Comment::query()->scopes('visible');
39     }
40
41     /**
42      * Create a new comment on an entity.
43      */
44     public function create(Entity $entity, string $html, ?int $parentId, string $contentRef): Comment
45     {
46         // Prevent comments being added to draft pages
47         if ($entity instanceof Page && $entity->draft) {
48             throw new \Exception(trans('errors.cannot_add_comment_to_draft'));
49         }
50
51         // Validate parent ID
52         if ($parentId !== null) {
53             $parentCommentExists = Comment::query()
54                 ->where('commentable_id', '=', $entity->id)
55                 ->where('commentable_type', '=', $entity->getMorphClass())
56                 ->where('local_id', '=', $parentId)
57                 ->exists();
58             if (!$parentCommentExists) {
59                 $parentId = null;
60             }
61         }
62
63         $userId = user()->id;
64         $comment = new Comment();
65
66         $comment->html = HtmlDescriptionFilter::filterFromString($html);
67         $comment->created_by = $userId;
68         $comment->updated_by = $userId;
69         $comment->local_id = $this->getNextLocalId($entity);
70         $comment->parent_id = $parentId;
71         $comment->content_ref = preg_match('/^bkmrk-(.*?):\d+:(\d*-\d*)?$/', $contentRef) === 1 ? $contentRef : '';
72
73         $entity->comments()->save($comment);
74         ActivityService::add(ActivityType::COMMENT_CREATE, $comment);
75         ActivityService::add(ActivityType::COMMENTED_ON, $entity);
76
77         $comment->refresh()->unsetRelations();
78         return $comment;
79     }
80
81     /**
82      * Update an existing comment.
83      */
84     public function update(Comment $comment, string $html): Comment
85     {
86         $comment->updated_by = user()->id;
87         $comment->html = HtmlDescriptionFilter::filterFromString($html);
88         $comment->save();
89
90         ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
91
92         return $comment;
93     }
94
95
96     /**
97      * Archive an existing comment.
98      */
99     public function archive(Comment $comment, bool $log = true): Comment
100     {
101         if ($comment->parent_id) {
102             throw new NotifyException('Only top-level comments can be archived.', '/', 400);
103         }
104
105         $comment->archived = true;
106         $comment->save();
107
108         if ($log) {
109             ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
110         }
111
112         return $comment;
113     }
114
115     /**
116      * Un-archive an existing comment.
117      */
118     public function unarchive(Comment $comment, bool $log = true): Comment
119     {
120         if ($comment->parent_id) {
121             throw new NotifyException('Only top-level comments can be un-archived.', '/', 400);
122         }
123
124         $comment->archived = false;
125         $comment->save();
126
127         if ($log) {
128             ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
129         }
130
131         return $comment;
132     }
133
134     /**
135      * Delete a comment from the system.
136      */
137     public function delete(Comment $comment): void
138     {
139         $comment->delete();
140
141         ActivityService::add(ActivityType::COMMENT_DELETE, $comment);
142     }
143
144     /**
145      * Get the next local ID relative to the linked entity.
146      */
147     protected function getNextLocalId(Entity $entity): int
148     {
149         $currentMaxId = $entity->comments()->max('local_id');
150
151         return $currentMaxId + 1;
152     }
153 }