PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.1.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.1.0
2.1.0 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 All 42 releases
fluent-boards / app / Http / Controllers / ReportController.php

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

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