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 / ReportController.php

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

193 lines 6.7 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\Services\PermissionManager;
6 use FluentBoards\App\Services\ReportService;
7 use FluentBoards\Framework\Http\Request\Request;
8 use FluentBoardsPro\App\Modules\TimeTracking\Model\TimeTrack;
9
10 class ReportController extends Controller
11 {
12 /**
13 * Reports → Overview aggregates for the selected board and date range.
14 */
15 public function getOverviewReport(Request $request)
16 {
17 return $this->sendReport($request, 'getOverviewReport');
18 }
19
20 /**
21 * Reports → Tasks aggregates for the selected board and date range.
22 */
23 public function getTasksReport(Request $request)
24 {
25 return $this->sendReport($request, 'getTasksReport');
26 }
27
28 /**
29 * Reports → Activity aggregates for the selected board and date range.
30 */
31 public function getActivityReport(Request $request)
32 {
33 return $this->sendReport($request, 'getActivityReport');
34 }
35
36 /**
37 * Reports → Roadmap aggregates for accessible roadmap boards.
38 */
39 public function getRoadmapReport(Request $request)
40 {
41 if (!defined('FLUENT_BOARDS_PRO_VERSION')) {
42 return $this->sendError(
43 esc_html__('This is a pro feature', 'fluent-boards'),
44 403
45 );
46 }
47
48 try {
49 $reportService = new ReportService();
50 $scope = $reportService->resolveRoadmapScope([
51 'board_id' => $this->getRequestedBoardId($request),
52 'start_date' => $request->getSafe('start_date', 'sanitize_text_field'),
53 'end_date' => $request->getSafe('end_date', 'sanitize_text_field'),
54 ]);
55
56 return $this->sendSuccess([
57 'report' => $reportService->getRoadmapReport($scope),
58 ], 200);
59 } catch (\Exception $e) {
60 return $this->sendError($e->getMessage(), 400);
61 }
62 }
63
64 /**
65 * The three report screens share their parameters and their envelope; only
66 * the aggregate they ask for differs.
67 */
68 private function sendReport(Request $request, $method)
69 {
70 try {
71 $reportService = new ReportService();
72
73 $scope = $reportService->resolveScope([
74 'board_id' => $this->getRequestedBoardId($request),
75 'start_date' => $request->getSafe('start_date', 'sanitize_text_field'),
76 'end_date' => $request->getSafe('end_date', 'sanitize_text_field'),
77 ]);
78
79 return $this->sendSuccess([
80 'report' => $reportService->{$method}($scope),
81 ], 200);
82 } catch (\Exception $e) {
83 return $this->sendError($e->getMessage(), 400);
84 }
85 }
86
87 /**
88 * Preserves a non-empty invalid board filter as an impossible ID so it
89 * cannot silently widen a report to every accessible board.
90 */
91 private function getRequestedBoardId(Request $request)
92 {
93 $rawBoardId = $request->get('board_id');
94 $boardId = $request->getSafe('board_id', 'intval');
95
96 if ($rawBoardId !== null && $rawBoardId !== '' && !$boardId) {
97 return -1;
98 }
99
100 return $boardId;
101 }
102
103 public function getTimeSheetReport(Request $request)
104 {
105 // Sanitize date inputs - validate they are valid date strings
106 $startDate = $request->getSafe('start_date', 'sanitize_text_field');
107 $endDate = $request->getSafe('end_date', 'sanitize_text_field');
108
109 // Validate date format (YYYY-MM-DD) and ensure dates are actually valid
110 if ($startDate) {
111 if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate)) {
112 $startDate = null;
113 } else {
114 // Validate that the date is actually valid (e.g., not "2024-13-45")
115 $dateParts = explode('-', $startDate);
116 if (count($dateParts) !== 3 || !checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0])) {
117 $startDate = null;
118 }
119 }
120 }
121 if ($endDate) {
122 if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
123 $endDate = null;
124 } else {
125 // Validate that the date is actually valid (e.g., not "2024-13-45")
126 $dateParts = explode('-', $endDate);
127 if (count($dateParts) !== 3 || !checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0])) {
128 $endDate = null;
129 }
130 }
131 }
132
133 $authUser = wp_get_current_user();
134 $boardIds = PermissionManager::getBoardIdsForUser($authUser->ID);
135 $boardId = $request->getSafe('board_id', 'intval');
136 if (!empty($boardId)) {
137 $boardIds = PermissionManager::getBoardIdsForUser($authUser->ID, $boardId);
138 }
139 $timings = TimeTrack::where('status', 'commited')->whereIn('board_id', $boardIds);
140 if ($startDate && $endDate) {
141 $timings = $timings->whereBetween('completed_at', [$startDate, $endDate]);
142 }
143 $timings = $timings->get();
144 $timings = $timings->load('task', 'user', 'board');
145
146 $sortTasks = [];
147 foreach ($timings as $timing) {
148 $times = $timings->where('task_id', $timing->task_id);
149
150 if (!isset($sortTasks[$timing['task_id']])) {
151 $sortTasks[$timing['task_id']] = [
152 'id' => $timing['task_id'],
153 'title' => $timing->task->title,
154 'board' => $timing->board,
155 'total' => 0,
156 'times' => []
157 ];
158 }
159
160 $sortTasks[$timing['task_id']]['total'] += $timing['billable_minutes'];
161
162 $formattedTimes = [];
163 foreach ($times as $time) {
164 $user = $time->user;
165
166 $formattedTimes[] = [
167 'id' => $time['id'],
168 'billable_minutes' => $time['billable_minutes'],
169 'working_minutes' => $time['working_minutes'],
170 'completed_at' => $time['completed_at'],
171 'message' => $time['message'],
172 'user' => [
173 'ID' => $user->ID,
174 'name' => $user->display_name,
175 'avatar' => fluent_boards_user_avatar($user->user_email),
176 'email' => $user->user_email
177 ]
178 ];
179
180 }
181 $sortTasks[$timing['task_id']]['times'] = $formattedTimes;
182 }
183
184 $sortTasks = array_values($sortTasks);
185
186 return $this->sendSuccess([
187 'message' => 'Time sheet report',
188 'timings' => $sortTasks
189 ], 200);
190 }
191
192 }
193