]> BookStack Code Mirror - bookstack/blob - app/Activity/Tools/CommentTree.php
Permissions: Cleanup after review of enum implementation PR
[bookstack] / app / Activity / Tools / CommentTree.php
1 <?php
2
3 namespace BookStack\Activity\Tools;
4
5 use BookStack\Activity\Models\Comment;
6 use BookStack\Entities\Models\Page;
7 use BookStack\Permissions\Permission;
8
9 class CommentTree
10 {
11     /**
12      * The built nested tree structure array.
13      * @var CommentTreeNode[]
14      */
15     protected array $tree;
16     protected array $comments;
17
18     public function __construct(
19         protected Page $page
20     ) {
21         $this->comments = $this->loadComments();
22         $this->tree = $this->createTree($this->comments);
23     }
24
25     public function enabled(): bool
26     {
27         return !setting('app-disable-comments');
28     }
29
30     public function empty(): bool
31     {
32         return count($this->getActive()) === 0;
33     }
34
35     public function count(): int
36     {
37         return count($this->comments);
38     }
39
40     public function getActive(): array
41     {
42         return array_filter($this->tree, fn (CommentTreeNode $node) => !$node->comment->archived);
43     }
44
45     public function activeThreadCount(): int
46     {
47         return count($this->getActive());
48     }
49
50     public function getArchived(): array
51     {
52         return array_filter($this->tree, fn (CommentTreeNode $node) => $node->comment->archived);
53     }
54
55     public function archivedThreadCount(): int
56     {
57         return count($this->getArchived());
58     }
59
60     public function getCommentNodeForId(int $commentId): ?CommentTreeNode
61     {
62         foreach ($this->tree as $node) {
63             if ($node->comment->id === $commentId) {
64                 return $node;
65             }
66         }
67
68         return null;
69     }
70
71     public function canUpdateAny(): bool
72     {
73         foreach ($this->comments as $comment) {
74             if (userCan(Permission::CommentUpdate, $comment)) {
75                 return true;
76             }
77         }
78
79         return false;
80     }
81
82     /**
83      * @param Comment[] $comments
84      * @return CommentTreeNode[]
85      */
86     protected function createTree(array $comments): array
87     {
88         $byId = [];
89         foreach ($comments as $comment) {
90             $byId[$comment->local_id] = $comment;
91         }
92
93         $childMap = [];
94         foreach ($comments as $comment) {
95             $parent = $comment->parent_id;
96             if (is_null($parent) || !isset($byId[$parent])) {
97                 $parent = 0;
98             }
99
100             if (!isset($childMap[$parent])) {
101                 $childMap[$parent] = [];
102             }
103             $childMap[$parent][] = $comment->local_id;
104         }
105
106         $tree = [];
107         foreach ($childMap[0] ?? [] as $childId) {
108             $tree[] = $this->createTreeNodeForId($childId, 0, $byId, $childMap);
109         }
110
111         return $tree;
112     }
113
114     protected function createTreeNodeForId(int $id, int $depth, array &$byId, array &$childMap): CommentTreeNode
115     {
116         $childIds = $childMap[$id] ?? [];
117         $children = [];
118
119         foreach ($childIds as $childId) {
120             $children[] = $this->createTreeNodeForId($childId, $depth + 1, $byId, $childMap);
121         }
122
123         return new CommentTreeNode($byId[$id], $depth, $children);
124     }
125
126     protected function loadComments(): array
127     {
128         if (!$this->enabled()) {
129             return [];
130         }
131
132         return $this->page->comments()
133             ->with('createdBy')
134             ->get()
135             ->all();
136     }
137 }