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

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

337 lines 10.4 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\User;
7 use FluentBoards\App\Services\Helper;
8 use FluentBoards\App\Services\PermissionManager;
9 use FluentBoards\App\Services\UserService;
10 use FluentBoards\Framework\Http\Request\Request;
11 use FluentCrm\App\Models\Subscriber;
12
13
14 class UserController extends Controller
15 {
16 private UserService $userService;
17
18 public function __construct(UserService $userService)
19 {
20 parent::__construct();
21 $this->userService = $userService;
22 }
23
24 public function allFluentBoardsUsers()
25 {
26 return [
27 'users' => $this->userService->allFluentBoardsUsers(),
28 'boards' => Board::select('id', 'title')->orderBy('title', 'ASC')->get()
29 ];
30 }
31
32 public function memberAssociatedTaskUsers($user_id)
33 {
34 $user_id = absint($user_id);
35 try {
36 $uniqueUsers = $this->userService->memberAssociatedTaskUsers($user_id);
37
38 return $this->sendSuccess([
39 'users' => $uniqueUsers['uniqueUsers'],
40 'userWiseBoardDesignation' => $uniqueUsers['userWiseBoardDesignation'],
41 ], 200);
42 } catch (\Exception $e) {
43 return $this->sendError($e->getMessage(), 404);
44 }
45 }
46
47 public function searchFluentBoardsUser(Request $request)
48 {
49 try {
50 $search_input = $request->getSafe('searchInput', 'sanitize_text_field', '');
51
52 $boardUsers = $this->userService->searchFluentBoardsUser($search_input);
53
54 return $this->sendSuccess($boardUsers, 200);
55 } catch (\Exception $e) {
56 return $this->sendError($e->getMessage(), 404);
57 }
58 }
59
60 public function searchMemberUser(Request $request, $user_id)
61 {
62 $user_id = absint($user_id);
63 try {
64 $search_input = $request->getSafe('searchInput', 'sanitize_text_field', '');
65 $searchResult = $this->userService->searchMemberUser($search_input, $user_id);
66
67 return $this->sendSuccess([
68 'users' => $searchResult,
69 ], 200);
70 } catch (\Exception $e) {
71 return $this->sendError($e->getMessage(), 404);
72 }
73 }
74
75 public function getMemberAssociatedTasks(Request $request, $user_id)
76 {
77 $user_id = absint($user_id);
78 // Sanitize boardIds array
79 $rawBoardIds = $request->getSafe('boardIds');
80 $boardIds = [];
81 if (is_array($rawBoardIds)) {
82 $boardIds = array_filter(array_map('intval', $rawBoardIds));
83 }
84
85 $perPage = max(1, min(absint($request->getSafe('per_page', 'intval', 15)), 50));
86
87 $requestData = [
88 'page' => $request->getSafe('page', 'intval', 1),
89 'taskType' => $request->getSafe('taskType', 'sanitize_text_field'),
90 'boardIds' => $boardIds,
91 'orderBy' => $request->getSafe('orderBy', 'sanitize_text_field'),
92 'order' => $request->getSafe('order', 'sanitize_text_field'),
93 'per_page' => $perPage
94 ];
95 try {
96 return $this->sendSuccess(
97 $this->userService->getMemberAssociatedTasks($user_id, $requestData)
98 , 200);
99 } catch (\Exception $e) {
100 return $this->sendError($e->getMessage(), 404);
101 }
102 }
103
104 /**
105 * Return profile task counts using the same categories shown on the dashboard.
106 */
107 public function getMemberTaskCounts(Request $request, $user_id)
108 {
109 $user_id = absint($user_id);
110 $rawBoardIds = $request->getSafe('boardIds');
111 $boardIds = is_array($rawBoardIds)
112 ? array_filter(array_map('intval', $rawBoardIds))
113 : [];
114
115 try {
116 return $this->sendSuccess([
117 'counts' => $this->userService->getMemberTaskCounts($user_id, $boardIds),
118 ], 200);
119 } catch (\Exception $e) {
120 return $this->sendError($e->getMessage(), 404);
121 }
122 }
123
124 public function getMemberRelatedAcitivies(Request $request, $user_id)
125 {
126 $user_id = absint($user_id);
127 $page = $request->getSafe('page', 'intval', 1);
128 try {
129 return $this->sendSuccess(
130 $this->userService->getMemberRelatedAcitivies($user_id, $page)
131 , 200);
132 } catch (\Exception $e) {
133 return $this->sendError($e->getMessage(), 404);
134 }
135 }
136
137 public function getMemberInfo($user_id)
138 {
139 if(!PermissionManager::isFluentBoardsUser($user_id)) {
140 return $this->sendError(
141 [
142 'message' => __('You are not authorized to access this resource', 'fluent-boards'),
143 'code' => 'fluent_boards_unauthorized',
144 'status' => 403
145 ],
146 403
147 );
148 }
149
150 $user_id = absint($user_id);
151 $user = User::findOrFail($user_id);
152
153 $user = Helper::sanitizeUserCollections($user);
154
155 $user->fbs_role = PermissionManager::isFluentBoardsAdmin($user_id) ? 'fbs_admin' : 'member';
156
157 $user->is_wp_admin = user_can($user_id, 'manage_options') ? 'yes' : 'no';
158
159 if (defined('FLUENTCRM')) {
160 $subscriber = Subscriber::where('user_id', $user_id)->first();
161 $user->fluentcrm_subscriber = $subscriber ?? null;
162 }
163
164 return [
165 'user' => $user
166 ];
167 }
168
169 public function getMemberBoards($user_id)
170 {
171 $user_id = absint($user_id);
172 try {
173 return $this->sendSuccess(
174 [
175 'boards' => $this->userService->getMemberBoards($user_id)
176 ], 200);
177 } catch (\Exception $e) {
178 return $this->sendError($e->getMessage(), 404);
179 }
180 }
181
182 public function getMemberStats($user_id)
183 {
184 $user_id = absint($user_id);
185
186 return $this->sendSuccess(
187 $this->userService->getMemberStats($user_id),
188 200
189 );
190 }
191
192 public function updateDisplayName(Request $request, $user_id)
193 {
194 $user_id = absint($user_id);
195 $currentUserId = get_current_user_id();
196
197 if ($currentUserId !== $user_id && !PermissionManager::isAdmin($currentUserId)) {
198 return $this->sendError(
199 __('You do not have permission to update this display name', 'fluent-boards'),
200 403
201 );
202 }
203
204 $displayName = $request->getSafe('display_name', 'sanitize_text_field');
205
206 if(!$displayName) {
207 return $this->sendError('Display name is required', 400);
208 }
209
210 $updateResult = wp_update_user([
211 'ID' => $user_id,
212 'display_name' => $displayName,
213 ]);
214
215 if (is_wp_error($updateResult)) {
216 return $this->sendError(
217 $updateResult->get_error_message(),
218 400
219 );
220 }
221
222 $user = User::findOrFail($user_id);
223 $user = Helper::sanitizeUserCollections($user);
224 $user->fbs_role = PermissionManager::isFluentBoardsAdmin($user_id) ? 'fbs_admin' : 'member';
225 $user->is_wp_admin = user_can($user_id, 'manage_options') ? 'yes' : 'no';
226
227 if (defined('FLUENTCRM')) {
228 $subscriber = Subscriber::where('user_id', $user_id)->first();
229 $user->fluentcrm_subscriber = $subscriber ?? null;
230 }
231
232 return $this->sendSuccess([
233 'message' => __('Display name has been updated', 'fluent-boards'),
234 'user' => $user,
235 ], 200);
236
237 }
238
239 public function updateProfilePhoto(Request $request, $user_id)
240 {
241 $user_id = absint($user_id);
242 $currentUserId = get_current_user_id();
243
244 // Only the user themself OR an admin can change the profile picture
245 if ($currentUserId !== $user_id && !PermissionManager::isAdmin($currentUserId)) {
246 return $this->sendError(
247 __('You do not have permission to update this profile photo', 'fluent-boards'),
248 403
249 );
250 }
251
252 // We’ll use native WordPress upload handling
253 if (empty($_FILES['photo']) || !empty($_FILES['photo']['error'])) {
254 return $this->sendError(
255 __('No photo uploaded or upload error', 'fluent-boards'),
256 400
257 );
258 }
259
260 // Limit file size to 2MB for profile photos
261 $maxSize = 2 * 1024 * 1024;
262 if ($_FILES['photo']['size'] > $maxSize) {
263 return $this->sendError(
264 __('Photo must be under 2MB', 'fluent-boards'),
265 400
266 );
267 }
268
269 $file = $_FILES['photo'];
270
271 // Validate MIME type server-side (client-sent type is spoofable)
272 $fileType = wp_check_filetype($file['name'], [
273 'jpg|jpeg|jpe' => 'image/jpeg',
274 'gif' => 'image/gif',
275 'png' => 'image/png',
276 'webp' => 'image/webp',
277 ]);
278
279 if (!$fileType['type']) {
280 return $this->sendError(
281 __('Invalid image type', 'fluent-boards'),
282 400
283 );
284 }
285
286 // Load WordPress upload helpers
287 if (!function_exists('wp_handle_upload')) {
288 require_once ABSPATH . 'wp-admin/includes/file.php';
289 }
290
291 $overrides = [
292 'test_form' => false,
293 'mimes' => [
294 'jpg|jpeg|jpe' => 'image/jpeg',
295 'gif' => 'image/gif',
296 'png' => 'image/png',
297 'webp' => 'image/webp',
298 ],
299 ];
300
301 $uploaded = wp_handle_upload($file, $overrides);
302
303 if (isset($uploaded['error'])) {
304 return $this->sendError(
305 $uploaded['error'],
306 400
307 );
308 }
309
310 $photoUrl = esc_url_raw($uploaded['url']);
311
312 // Store custom profile photo in user meta
313 update_user_meta($user_id, 'fbs_profile_photo', $photoUrl);
314
315 // Reload user and sanitize same as in getMemberInfo()
316 $user = User::findOrFail($user_id);
317 $user = Helper::sanitizeUserCollections($user);
318
319 // Override photo field if your sanitizer does not already use the meta
320 $user->photo = $photoUrl;
321
322 $user->fbs_role = PermissionManager::isFluentBoardsAdmin($user_id) ? 'fbs_admin' : 'member';
323 $user->is_wp_admin = user_can($user_id, 'manage_options') ? 'yes' : 'no';
324
325 if (defined('FLUENTCRM')) {
326 $subscriber = Subscriber::where('user_id', $user_id)->first();
327 $user->fluentcrm_subscriber = $subscriber ?? null;
328 }
329
330 return $this->sendSuccess([
331 'message' => __('Profile photo has been updated', 'fluent-boards'),
332 'photo_url' => $photoUrl,
333 'user' => $user,
334 ], 200);
335 }
336 }
337