PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / trunk
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration vtrunk
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 trunk, at app/Http/Controllers/ReportController.php

203 lines 7.0 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 /**
104 * Returns the timesheet report for accessible boards when Pro is active.
105 */
106 public function getTimeSheetReport(Request $request)
107 {
108 if (!defined('FLUENT_BOARDS_PRO_VERSION')) {
109 return $this->sendError(
110 esc_html__('This is a pro feature', 'fluent-boards'),
111 403
112 );
113 }
114
115 // Sanitize date inputs - validate they are valid date strings
116 $startDate = $request->getSafe('start_date', 'sanitize_text_field');
117 $endDate = $request->getSafe('end_date', 'sanitize_text_field');
118
119 // Validate date format (YYYY-MM-DD) and ensure dates are actually valid
120 if ($startDate) {
121 if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate)) {
122 $startDate = null;
123 } else {
124 // Validate that the date is actually valid (e.g., not "2024-13-45")
125 $dateParts = explode('-', $startDate);
126 if (count($dateParts) !== 3 || !checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0])) {
127 $startDate = null;
128 }
129 }
130 }
131 if ($endDate) {
132 if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
133 $endDate = null;
134 } else {
135 // Validate that the date is actually valid (e.g., not "2024-13-45")
136 $dateParts = explode('-', $endDate);
137 if (count($dateParts) !== 3 || !checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0])) {
138 $endDate = null;
139 }
140 }
141 }
142
143 $authUser = wp_get_current_user();
144 $boardIds = PermissionManager::getBoardIdsForUser($authUser->ID);
145 $boardId = $request->getSafe('board_id', 'intval');
146 if (!empty($boardId)) {
147 $boardIds = PermissionManager::getBoardIdsForUser($authUser->ID, $boardId);
148 }
149 $timings = TimeTrack::where('status', 'commited')->whereIn('board_id', $boardIds);
150 if ($startDate && $endDate) {
151 $timings = $timings->whereBetween('completed_at', [$startDate, $endDate]);
152 }
153 $timings = $timings->get();
154 $timings = $timings->load('task', 'user', 'board');
155
156 $sortTasks = [];
157 foreach ($timings as $timing) {
158 $times = $timings->where('task_id', $timing->task_id);
159
160 if (!isset($sortTasks[$timing['task_id']])) {
161 $sortTasks[$timing['task_id']] = [
162 'id' => $timing['task_id'],
163 'title' => $timing->task->title,
164 'board' => $timing->board,
165 'total' => 0,
166 'times' => []
167 ];
168 }
169
170 $sortTasks[$timing['task_id']]['total'] += $timing['billable_minutes'];
171
172 $formattedTimes = [];
173 foreach ($times as $time) {
174 $user = $time->user;
175
176 $formattedTimes[] = [
177 'id' => $time['id'],
178 'billable_minutes' => $time['billable_minutes'],
179 'working_minutes' => $time['working_minutes'],
180 'completed_at' => $time['completed_at'],
181 'message' => $time['message'],
182 'user' => [
183 'ID' => $user->ID,
184 'name' => $user->display_name,
185 'avatar' => fluent_boards_user_avatar($user->user_email),
186 'email' => $user->user_email
187 ]
188 ];
189
190 }
191 $sortTasks[$timing['task_id']]['times'] = $formattedTimes;
192 }
193
194 $sortTasks = array_values($sortTasks);
195
196 return $this->sendSuccess([
197 'message' => 'Time sheet report',
198 'timings' => $sortTasks
199 ], 200);
200 }
201
202 }
203