]> BookStack Code Mirror - bookstack/blob - app/Activity/CommentRepo.php
API: Added comment CUD endpoints, drafted tests
[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      */
35     public function getQueryForVisible(): Builder
36     {
37         return Comment::query()->scopes('visible');
38     }
39
40     /**
41      * Create a new comment on an entity.
42      */
43     public function create(Entity $entity, string $html, ?int $parentId, string $contentRef): Comment
44     {
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'));
48         }
49
50         // Validate parent ID
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)
56                 ->exists();
57             if (!$parentCommentExists) {
58                 $parentId = null;
59             }
60         }
61
62         $userId = user()->id;
63         $comment = new Comment();
64
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 : '';
71
72         $entity->comments()->save($comment);
73         ActivityService::add(ActivityType::COMMENT_CREATE, $comment);
74         ActivityService::add(ActivityType::COMMENTED_ON, $entity);
75
76         return $comment;
77     }
78
79     /**
80      * Update an existing comment.
81      */
82     public function update(Comment $comment, string $html): Comment
83     {
84         $comment->updated_by = user()->id;
85         $comment->html = HtmlDescriptionFilter::filterFromString($html);
86         $comment->save();
87
88         ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
89
90         return $comment;
91     }
92
93
94     /**
95      * Archive an existing comment.
96      */
97     public function archive(Comment $comment, bool $log = true): Comment
98     {
99         if ($comment->parent_id) {
100             throw new NotifyException('Only top-level comments can be archived.', '/', 400);
101         }
102
103         $comment->archived = true;
104         $comment->save();
105
106         if ($log) {
107             ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
108         }
109
110         return $comment;
111     }
112
113     /**
114      * Un-archive an existing comment.
115      */
116     public function unarchive(Comment $comment, bool $log = true): Comment
117     {
118         if ($comment->parent_id) {
119             throw new NotifyException('Only top-level comments can be un-archived.', '/', 400);
120         }
121
122         $comment->archived = false;
123         $comment->save();
124
125         if ($log) {
126             ActivityService::add(ActivityType::COMMENT_UPDATE, $comment);
127         }
128
129         return $comment;
130     }
131
132     /**
133      * Delete a comment from the system.
134      */
135     public function delete(Comment $comment): void
136     {
137         $comment->delete();
138
139         ActivityService::add(ActivityType::COMMENT_DELETE, $comment);
140     }
141
142     /**
143      * Get the next local ID relative to the linked entity.
144      */
145     protected function getNextLocalId(Entity $entity): int
146     {
147         $currentMaxId = $entity->comments()->max('local_id');
148
149         return $currentMaxId + 1;
150     }
151 }