PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.0.12
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.0.12
2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 1.45 All 41 releases
fluent-boards / app / Http / Controllers / PublicBoardController.php

PublicBoardController.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 2.0.12, at app/Http/Controllers/PublicBoardController.php

270 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\App\Http\Controllers;
4
5 use FluentBoards\App\Models\Board;
6 use FluentBoards\App\Models\Stage;
7 use FluentBoards\App\Models\Task;
8 use FluentBoards\App\Services\DescriptionMarkdownConverter;
9 use FluentBoards\App\Services\PublicAccessService;
10 use FluentBoards\Framework\Http\Request\Request;
11
12 class PublicBoardController extends Controller
13 {
14 public function find($board_id)
15 {
16 $board_id = absint($board_id);
17 $board = Board::findOrFail($board_id);
18
19 $board->background = maybe_unserialize($board->background);
20 $board->description = DescriptionMarkdownConverter::normalize($board->description);
21 $board->createdOn = $board->created_at ? $board->created_at->format('Y-m-d') : null;
22 $board->load(['stages', 'labels', 'users']);
23
24 $board->users = PublicAccessService::sanitizeUsers($board->users);
25 $board->isUserOnlyViewer = true;
26 $board->is_pinned = false;
27
28 $board->makeHidden([
29 'settings', 'currency', 'crm_contact_id',
30 'updated_at', 'created_by',
31 ]);
32
33 return [
34 'board' => $board
35 ];
36 }
37
38 public function getTasksByBoard($board_id)
39 {
40 $board_id = absint($board_id);
41 Board::findOrFail($board_id);
42
43 $stageIds = $this->getStageIdsByBoard($board_id);
44
45 $tasks = Task::with(['assignees', 'labels'])
46 ->where('board_id', $board_id)
47 ->whereNull('archived_at')
48 ->whereNull('parent_id')
49 ->whereIn('stage_id', $stageIds)
50 ->orderBy('due_at', 'ASC')
51 ->get();
52
53 $this->sanitizeTasks($tasks);
54
55 return [
56 'tasks' => $tasks,
57 ];
58 }
59
60 public function getTasksByBoardStage($board_id)
61 {
62 $board_id = absint($board_id);
63 Board::findOrFail($board_id);
64
65 $tasks = [];
66 $stageIds = $this->getStageIdsByBoard($board_id);
67 $stageTaskCounts = $this->getStageTaskCounts($board_id, $stageIds);
68 $paginationByStage = [];
69
70 foreach ($stageIds as $stageId) {
71 $stageTasks = Task::with(['assignees', 'labels'])
72 ->where('board_id', $board_id)
73 ->where('stage_id', $stageId)
74 ->whereNull('archived_at')
75 ->whereNull('parent_id')
76 ->orderBy('position', 'ASC')
77 ->limit(20)
78 ->get();
79
80 $this->sanitizeTasks($stageTasks);
81 $tasks = array_merge($tasks, $stageTasks->toArray());
82
83 $startCursor = $stageTasks->count() ? (float) $stageTasks->first()->position : null;
84 $endCursor = $stageTasks->count() ? (float) $stageTasks->last()->position : null;
85 $loadedCount = $stageTasks->count();
86 $hasMoreAfter = (int) ($stageTaskCounts[$stageId] ?? 0) > $loadedCount;
87
88 $paginationByStage[$stageId] = [
89 'stage_id' => (int) $stageId,
90 'total_count' => (int) ($stageTaskCounts[$stageId] ?? 0),
91 'limit' => 20,
92 'direction' => 'next',
93 'cursor' => null,
94 'has_more' => $hasMoreAfter,
95 'has_more_before' => false,
96 'has_more_after' => $hasMoreAfter,
97 'start_cursor' => $startCursor,
98 'end_cursor' => $endCursor,
99 ];
100 }
101
102 return [
103 'tasks' => $tasks,
104 'pagination_by_stage' => $paginationByStage
105 ];
106 }
107
108 public function getStageTasksPage(Request $request, $board_id)
109 {
110 $board_id = absint($board_id);
111 Board::findOrFail($board_id);
112
113 $stageId = $request->getSafe('stage_id', 'intval');
114 $limit = $request->getSafe('limit', 'intval', 20);
115 $direction = $request->getSafe('direction', 'sanitize_text_field', 'next');
116 $cursor = $request->getSafe('cursor', 'floatval');
117
118 if (!$stageId) {
119 return $this->sendError(esc_html__('Invalid Stage', 'fluent-boards'), 400);
120 }
121
122 if (!in_array($direction, ['next', 'prev'], true)) {
123 return $this->sendError(esc_html__('Invalid direction', 'fluent-boards'), 400);
124 }
125
126 $limit = max(1, min(100, $limit));
127
128 $stage = Stage::where('board_id', $board_id)
129 ->where('id', $stageId)
130 ->whereNull('archived_at')
131 ->first();
132
133 if (!$stage) {
134 return $this->sendError(esc_html__('Stage not found', 'fluent-boards'), 404);
135 }
136
137 $stageTasksQuery = $this->makeStageTasksQuery($board_id, $stageId);
138
139 if ($cursor !== null) {
140 if ($direction === 'prev') {
141 $stageTasksQuery->where('position', '<', $cursor);
142 } else {
143 $stageTasksQuery->where('position', '>', $cursor);
144 }
145 }
146
147 $stageTasks = $stageTasksQuery
148 ->orderBy('position', $direction === 'prev' ? 'DESC' : 'ASC')
149 ->limit($limit + 1)
150 ->get();
151
152 $hasMoreInDirection = $stageTasks->count() > $limit;
153 if ($hasMoreInDirection) {
154 $stageTasks = $stageTasks->slice(0, $limit)->values();
155 }
156
157 if ($direction === 'prev') {
158 $stageTasks = $stageTasks->sortBy('position')->values();
159 }
160
161 $this->sanitizeTasks($stageTasks);
162
163 $startCursor = $stageTasks->count() ? (float) $stageTasks->first()->position : null;
164 $endCursor = $stageTasks->count() ? (float) $stageTasks->last()->position : null;
165
166 $hasMoreBefore = false;
167 $hasMoreAfter = false;
168
169 if ($startCursor !== null) {
170 $hasMoreBefore = $this->makeStageTasksQuery($board_id, $stageId)
171 ->where('position', '<', $startCursor)
172 ->exists();
173 }
174
175 if ($endCursor !== null) {
176 $hasMoreAfter = $this->makeStageTasksQuery($board_id, $stageId)
177 ->where('position', '>', $endCursor)
178 ->exists();
179 }
180
181 return [
182 'tasks' => $stageTasks,
183 'pagination' => [
184 'stage_id' => (int) $stageId,
185 'limit' => (int) $limit,
186 'direction' => $direction,
187 'cursor' => $cursor !== null ? (float) $cursor : null,
188 'has_more' => $hasMoreInDirection,
189 'has_more_before' => $hasMoreBefore,
190 'has_more_after' => $hasMoreAfter,
191 'start_cursor' => $startCursor,
192 'end_cursor' => $endCursor,
193 ],
194 ];
195 }
196
197 private function getStageIdsByBoard($board_id)
198 {
199 return Stage::where('board_id', $board_id)
200 ->whereNull('archived_at')
201 ->pluck('id')
202 ->toArray();
203 }
204
205 private function getStageTaskCounts($board_id, array $stageIds)
206 {
207 if (!$stageIds) {
208 return [];
209 }
210
211 return Task::query()
212 ->selectRaw('stage_id, COUNT(*) as total_count')
213 ->where('board_id', $board_id)
214 ->whereNull('archived_at')
215 ->whereNull('parent_id')
216 ->whereIn('stage_id', $stageIds)
217 ->groupBy('stage_id')
218 ->pluck('total_count', 'stage_id')
219 ->map(function ($count) {
220 return (int) $count;
221 })
222 ->toArray();
223 }
224
225 private function makeStageTasksQuery($board_id, $stageId)
226 {
227 return Task::query()
228 ->with(['assignees', 'labels'])
229 ->where('board_id', $board_id)
230 ->where('stage_id', $stageId)
231 ->whereNull('archived_at')
232 ->whereNull('parent_id');
233 }
234
235 private function sanitizeTasks($tasks)
236 {
237 $sensitiveFields = [
238 'description',
239 'crm_contact_id',
240 'lead_value',
241 'created_by',
242 'source',
243 'source_id',
244 'settings',
245 'reminder_type',
246 'remind_at',
247 'slug',
248 'events',
249 'started_at',
250 'last_completed_at',
251 'comments_count',
252 'archived_at',
253 'parent_id',
254 'type',
255 ];
256
257 foreach ($tasks as $task) {
258 $task->setAppends([]);
259 $task->makeHidden($sensitiveFields);
260 $task->isOverdue = $task->isOverdue();
261 $task->isUpcoming = $task->upcoming();
262 $task->is_watching = false;
263 $task->contact = null;
264 $task->notifications = [];
265 $task->watchers = [];
266 $task->assignees = PublicAccessService::sanitizeUsers($task->assignees);
267 }
268 }
269 }
270