]> BookStack Code Mirror - bookstack/blob - app/Activity/Tools/CommentTree.php
Copying: Fixed issue with non-page links to page permalinks
[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
17     /**
18      * A linear array of loaded comments.
19      * @var Comment[]
20      */
21     protected array $comments;
22
23     public function __construct(
24         protected Page $page
25     ) {
26         $this->comments = $this->loadComments();
27         $this->tree = $this->createTree($this->comments);
28     }
29
30     public function enabled(): bool
31     {
32         return !setting('app-disable-comments');
33     }
34
35     public function empty(): bool
36     {
37         return count($this->getActive()) === 0;
38     }
39
40     public function count(): int
41     {
42         return count($this->comments);
43     }
44
45     public function getActive(): array
46     {
47         return array_values(array_filter($this->tree, fn (CommentTreeNode $node) => !$node->comment->archived));
48     }
49
50     public function activeThreadCount(): int
51     {
52         return count($this->getActive());
53     }
54
55     public function getArchived(): array
56     {
57         return array_values(array_filter($this->tree, fn (CommentTreeNode $node) => $node->comment->archived));
58     }
59
60     public function archivedThreadCount(): int
61     {
62         return count($this->getArchived());
63     }
64
65     public function getCommentNodeForId(int $commentId): ?CommentTreeNode
66     {
67         foreach ($this->tree as $node) {
68             if ($node->comment->id === $commentId) {
69                 return $node;
70             }
71         }
72
73         return null;
74     }
75
76     public function canUpdateAny(): bool
77     {
78         foreach ($this->comments as $comment) {
79             if (userCan(Permission::CommentUpdate, $comment)) {
80                 return true;
81             }
82         }
83
84         return false;
85     }
86
87     public function loadVisibleHtml(): void
88     {
89         foreach ($this->comments as $comment) {
90             $comment->setAttribute('html', $comment->safeHtml());
91             $comment->makeVisible('html');
92         }
93     }
94
95     /**
96      * @param Comment[] $comments
97      * @return CommentTreeNode[]
98      */
99     protected function createTree(array $comments): array
100     {
101         $byId = [];
102         foreach ($comments as $comment) {
103             $byId[$comment->local_id] = $comment;
104         }
105
106         $childMap = [];
107         foreach ($comments as $comment) {
108             $parent = $comment->parent_id;
109             if (is_null($parent) || !isset($byId[$parent])) {
110                 $parent = 0;
111             }
112
113             if (!isset($childMap[$parent])) {
114                 $childMap[$parent] = [];
115             }
116             $childMap[$parent][] = $comment->local_id;
117         }
118
119         $tree = [];
120         foreach ($childMap[0] ?? [] as $childId) {
121             $tree[] = $this->createTreeNodeForId($childId, 0, $byId, $childMap);
122         }
123
124         return $tree;
125     }
126
127     protected function createTreeNodeForId(int $id, int $depth, array &$byId, array &$childMap): CommentTreeNode
128     {
129         $childIds = $childMap[$id] ?? [];
130         $children = [];
131
132         foreach ($childIds as $childId) {
133             $children[] = $this->createTreeNodeForId($childId, $depth + 1, $byId, $childMap);
134         }
135
136         return new CommentTreeNode($byId[$id], $depth, $children);
137     }
138
139     /**
140      * @return Comment[]
141      */
142     protected function loadComments(): array
143     {
144         if (!$this->enabled()) {
145             return [];
146         }
147
148         return $this->page->comments()
149             ->with('createdBy')
150             ->get()
151             ->all();
152     }
153 }